ng_base.c revision 229003
1139823Simp/*-
252419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
352419Sjulian * All rights reserved.
470700Sjulian *
552419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
652419Sjulian * redistribution of this software, in source or object code forms, with or
752419Sjulian * without modifications are expressly permitted by Whistle Communications;
852419Sjulian * provided, however, that:
952419Sjulian * 1. Any and all reproductions of the source or object code must include the
1052419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1152419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1252419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1352419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1452419Sjulian *    such appears in the above copyright notice or in the software.
1570700Sjulian *
1652419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
1752419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
1852419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
1952419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2052419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2152419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2252419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2352419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2452419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2552419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
2652419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
2752419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
2852419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
2952419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3052419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3152419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3252419Sjulian * OF SUCH DAMAGE.
3352419Sjulian *
3467506Sjulian * Authors: Julian Elischer <julian@freebsd.org>
3567506Sjulian *          Archie Cobbs <archie@freebsd.org>
3652419Sjulian *
3752419Sjulian * $FreeBSD: head/sys/netgraph/ng_base.c 229003 2011-12-30 15:41:28Z glebius $
3852419Sjulian * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
3952419Sjulian */
4052419Sjulian
4152419Sjulian/*
4252419Sjulian * This file implements the base netgraph code.
4352419Sjulian */
4452419Sjulian
4552419Sjulian#include <sys/param.h>
46139236Sglebius#include <sys/systm.h>
47139235Sglebius#include <sys/ctype.h>
4852419Sjulian#include <sys/errno.h>
49131933Smarcel#include <sys/kdb.h>
5052419Sjulian#include <sys/kernel.h>
51154225Sglebius#include <sys/ktr.h>
52114216Skan#include <sys/limits.h>
5352419Sjulian#include <sys/malloc.h>
54139235Sglebius#include <sys/mbuf.h>
5552419Sjulian#include <sys/queue.h>
5672946Sjulian#include <sys/sysctl.h>
57139235Sglebius#include <sys/syslog.h>
58172806Smav#include <sys/refcount.h>
59175847Smav#include <sys/proc.h>
60186093Smav#include <sys/unistd.h>
61186093Smav#include <sys/kthread.h>
62186093Smav#include <sys/smp.h>
63177953Smav#include <machine/cpu.h>
6452419Sjulian
6552419Sjulian#include <net/netisr.h>
66195699Srwatson#include <net/vnet.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	0,	/* numhooks */
11770935Sjulian	NULL,	/* private */
11870935Sjulian	0,	/* ID */
119201145Santoine	LIST_HEAD_INITIALIZER(ng_deadnode.nd_hooks),
12070935Sjulian	{},	/* all_nodes list entry */
12170935Sjulian	{},	/* id hashtable list entry */
12270935Sjulian	{	0,
123178228Smav		0,
12470935Sjulian		{}, /* should never use! (should hang) */
125178228Smav		{}, /* workqueue entry */
126178228Smav		STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue),
12770935Sjulian	},
128178228Smav	1,	/* refs */
129191827Szec	NULL,	/* vnet */
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,
142148261Sglebius	0,		/* undefined data link type */
14370935Sjulian	&ng_deadhook,	/* Peer is self */
14470935Sjulian	&ng_deadnode,	/* attached to deadnode */
14570935Sjulian	{},		/* hooks list */
14671885Sjulian	NULL,		/* override rcvmsg() */
14771885Sjulian	NULL,		/* override rcvdata() */
148178228Smav	1,		/* refs always >= 1 */
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 */
161178228Smavstatic STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_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 */
170215701Sdimstatic VNET_DEFINE(LIST_HEAD(, ng_node), ng_ID_hash[NG_ID_HASH_SIZE]);
171195727Srwatson#define	V_ng_ID_hash			VNET(ng_ID_hash)
172195699Srwatson
17370700Sjulianstatic struct mtx	ng_idhash_mtx;
17471354Sjulian/* Method to find a node.. used twice so do it here */
17571354Sjulian#define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE))
17671354Sjulian#define NG_IDHASH_FIND(ID, node)					\
17771354Sjulian	do { 								\
178131008Srwatson		mtx_assert(&ng_idhash_mtx, MA_OWNED);			\
179181803Sbz		LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)],	\
18071354Sjulian						nd_idnodes) {		\
18171354Sjulian			if (NG_NODE_IS_VALID(node)			\
18271354Sjulian			&& (NG_NODE_ID(node) == ID)) {			\
18371354Sjulian				break;					\
18471354Sjulian			}						\
18571354Sjulian		}							\
18671354Sjulian	} while (0)
18752722Sjulian
188215701Sdimstatic VNET_DEFINE(LIST_HEAD(, ng_node), ng_name_hash[NG_NAME_HASH_SIZE]);
189195727Srwatson#define	V_ng_name_hash			VNET(ng_name_hash)
190195699Srwatson
191176802Smavstatic struct mtx	ng_namehash_mtx;
192176802Smav#define NG_NAMEHASH(NAME, HASH)				\
193176802Smav	do {						\
194176802Smav		u_char	h = 0;				\
195176802Smav		const u_char	*c;			\
196176802Smav		for (c = (const u_char*)(NAME); *c; c++)\
197176802Smav			h += *c;			\
198176802Smav		(HASH) = h % (NG_NAME_HASH_SIZE);	\
199176802Smav	} while (0)
20070700Sjulian
201176802Smav
20252419Sjulian/* Internal functions */
20352419Sjulianstatic int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
20470700Sjulianstatic int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
20552722Sjulianstatic ng_ID_t	ng_decodeidname(const char *name);
20652419Sjulianstatic int	ngb_mod_event(module_t mod, int event, void *data);
207177953Smavstatic void	ng_worklist_add(node_p node);
208186093Smavstatic void	ngthread(void *);
209170180Sglebiusstatic int	ng_apply_item(node_p node, item_p item, int rw);
210178228Smavstatic void	ng_flush_input_queue(node_p node);
21170700Sjulianstatic node_p	ng_ID2noderef(ng_ID_t ID);
212172806Smavstatic int	ng_con_nodes(item_p item, node_p node, const char *name,
213172806Smav		    node_p node2, const char *name2);
214172806Smavstatic int	ng_con_part2(node_p node, item_p item, hook_p hook);
215172806Smavstatic int	ng_con_part3(node_p node, item_p item, hook_p hook);
21671047Sjulianstatic int	ng_mkpeer(node_p node, const char *name,
21771047Sjulian						const char *name2, char *type);
21852419Sjulian
219152451Sglebius/* Imported, these used to be externally visible, some may go back. */
22070700Sjulianvoid	ng_destroy_hook(hook_p hook);
22170700Sjulianint	ng_path2noderef(node_p here, const char *path,
22270700Sjulian	node_p *dest, hook_p *lasthook);
22370700Sjulianint	ng_make_node(const char *type, node_p *nodepp);
22470700Sjulianint	ng_path_parse(char *addr, char **node, char **path, char **hook);
22571849Sjulianvoid	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
22670784Sjulianvoid	ng_unname(node_p node);
22770700Sjulian
22870700Sjulian
22952419Sjulian/* Our own netgraph malloc type */
23052419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
23170700SjulianMALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
232227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook",
233227293Sed    "netgraph hook structures");
234227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node",
235227293Sed    "netgraph node structures");
236227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item",
237227293Sed    "netgraph item structures");
23852419Sjulian
23970700Sjulian/* Should not be visible outside this file */
24070784Sjulian
24170784Sjulian#define _NG_ALLOC_HOOK(hook) \
242184205Sdes	hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
24370784Sjulian#define _NG_ALLOC_NODE(node) \
244184205Sdes	node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
24570784Sjulian
246168049Swkoszek#define	NG_QUEUE_LOCK_INIT(n)			\
247168137Swkoszek	mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF)
248168049Swkoszek#define	NG_QUEUE_LOCK(n)			\
249168137Swkoszek	mtx_lock(&(n)->q_mtx)
250168049Swkoszek#define	NG_QUEUE_UNLOCK(n)			\
251168137Swkoszek	mtx_unlock(&(n)->q_mtx)
252168049Swkoszek#define	NG_WORKLIST_LOCK_INIT()			\
253168137Swkoszek	mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF)
254168049Swkoszek#define	NG_WORKLIST_LOCK()			\
255168137Swkoszek	mtx_lock(&ng_worklist_mtx)
256168049Swkoszek#define	NG_WORKLIST_UNLOCK()			\
257168137Swkoszek	mtx_unlock(&ng_worklist_mtx)
258186093Smav#define	NG_WORKLIST_SLEEP()			\
259186093Smav	mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0)
260186093Smav#define	NG_WORKLIST_WAKEUP()			\
261186093Smav	wakeup_one(&ng_worklist)
262168049Swkoszek
26370784Sjulian#ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
26470784Sjulian/*
26570784Sjulian * In debug mode:
26670784Sjulian * In an attempt to help track reference count screwups
26770784Sjulian * we do not free objects back to the malloc system, but keep them
26870784Sjulian * in a local cache where we can examine them and keep information safely
26970784Sjulian * after they have been freed.
27070784Sjulian * We use this scheme for nodes and hooks, and to some extent for items.
27170784Sjulian */
27270784Sjulianstatic __inline hook_p
27370784Sjulianng_alloc_hook(void)
27470784Sjulian{
27570784Sjulian	hook_p hook;
27670784Sjulian	SLIST_ENTRY(ng_hook) temp;
27772200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
27870784Sjulian	hook = LIST_FIRST(&ng_freehooks);
27970784Sjulian	if (hook) {
28070784Sjulian		LIST_REMOVE(hook, hk_hooks);
28170784Sjulian		bcopy(&hook->hk_all, &temp, sizeof(temp));
28270784Sjulian		bzero(hook, sizeof(struct ng_hook));
28370784Sjulian		bcopy(&temp, &hook->hk_all, sizeof(temp));
28472200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
28570784Sjulian		hook->hk_magic = HK_MAGIC;
28670784Sjulian	} else {
28772200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
28870784Sjulian		_NG_ALLOC_HOOK(hook);
28970784Sjulian		if (hook) {
29070784Sjulian			hook->hk_magic = HK_MAGIC;
29172200Sbmilekic			mtx_lock(&ng_nodelist_mtx);
29270784Sjulian			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
29372200Sbmilekic			mtx_unlock(&ng_nodelist_mtx);
29470784Sjulian		}
29570784Sjulian	}
29670784Sjulian	return (hook);
29770784Sjulian}
29870784Sjulian
29970784Sjulianstatic __inline node_p
30070784Sjulianng_alloc_node(void)
30170784Sjulian{
30270784Sjulian	node_p node;
30370784Sjulian	SLIST_ENTRY(ng_node) temp;
30472200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
30570784Sjulian	node = LIST_FIRST(&ng_freenodes);
30670784Sjulian	if (node) {
30770784Sjulian		LIST_REMOVE(node, nd_nodes);
30870784Sjulian		bcopy(&node->nd_all, &temp, sizeof(temp));
30970784Sjulian		bzero(node, sizeof(struct ng_node));
31070784Sjulian		bcopy(&temp, &node->nd_all, sizeof(temp));
31172200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
31270784Sjulian		node->nd_magic = ND_MAGIC;
31370784Sjulian	} else {
31472200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
31570784Sjulian		_NG_ALLOC_NODE(node);
31670784Sjulian		if (node) {
31770784Sjulian			node->nd_magic = ND_MAGIC;
31872200Sbmilekic			mtx_lock(&ng_nodelist_mtx);
31970784Sjulian			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
32072200Sbmilekic			mtx_unlock(&ng_nodelist_mtx);
32170784Sjulian		}
32270784Sjulian	}
32370784Sjulian	return (node);
32470784Sjulian}
32570784Sjulian
32670784Sjulian#define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
32770784Sjulian#define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
32870784Sjulian
32970784Sjulian
33070784Sjulian#define NG_FREE_HOOK(hook)						\
33170784Sjulian	do {								\
332229003Sglebius		mtx_lock(&ng_nodelist_mtx);				\
33370784Sjulian		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
33470784Sjulian		hook->hk_magic = 0;					\
335229003Sglebius		mtx_unlock(&ng_nodelist_mtx);				\
33670784Sjulian	} while (0)
33770784Sjulian
33870784Sjulian#define NG_FREE_NODE(node)						\
33970784Sjulian	do {								\
340229003Sglebius		mtx_lock(&ng_nodelist_mtx);				\
34170784Sjulian		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
34270784Sjulian		node->nd_magic = 0;					\
343229003Sglebius		mtx_unlock(&ng_nodelist_mtx);				\
34470784Sjulian	} while (0)
34570784Sjulian
34670784Sjulian#else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
34770784Sjulian
34870784Sjulian#define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
34970784Sjulian#define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
35070784Sjulian
351184205Sdes#define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0)
352184205Sdes#define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0)
35370784Sjulian
35470784Sjulian#endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
35570784Sjulian
356131933Smarcel/* Set this to kdb_enter("X") to catch all errors as they occur */
35752419Sjulian#ifndef TRAP_ERROR
35871047Sjulian#define TRAP_ERROR()
35952419Sjulian#endif
36052419Sjulian
361215701Sdimstatic VNET_DEFINE(ng_ID_t, nextID) = 1;
362195727Srwatson#define	V_nextID			VNET(nextID)
36352722Sjulian
36453403Sarchie#ifdef INVARIANTS
36553403Sarchie#define CHECK_DATA_MBUF(m)	do {					\
36653403Sarchie		struct mbuf *n;						\
36753403Sarchie		int total;						\
36853403Sarchie									\
369113255Sdes		M_ASSERTPKTHDR(m);					\
370149818Sglebius		for (total = 0, n = (m); n != NULL; n = n->m_next) {	\
37153403Sarchie			total += n->m_len;				\
372149818Sglebius			if (n->m_nextpkt != NULL)			\
373149818Sglebius				panic("%s: m_nextpkt", __func__);	\
374149818Sglebius		}							\
375149827Sglebius									\
37653403Sarchie		if ((m)->m_pkthdr.len != total) {			\
37753403Sarchie			panic("%s: %d != %d",				\
37887599Sobrien			    __func__, (m)->m_pkthdr.len, total);	\
37953403Sarchie		}							\
38053403Sarchie	} while (0)
38153403Sarchie#else
38253403Sarchie#define CHECK_DATA_MBUF(m)
38353403Sarchie#endif
38452722Sjulian
385172806Smav#define ERROUT(x)	do { error = (x); goto done; } while (0)
38653403Sarchie
38752419Sjulian/************************************************************************
38853913Sarchie	Parse type definitions for generic messages
38953913Sarchie************************************************************************/
39053913Sarchie
39153913Sarchie/* Handy structure parse type defining macro */
39253913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
39397685Sarchiestatic const struct ng_parse_struct_field				\
39497685Sarchie	ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args;	\
39553913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
39653913Sarchie	&ng_parse_struct_type,						\
39797685Sarchie	&ng_ ## lo ## _type_fields					\
39853913Sarchie}
39953913Sarchie
40053913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
40153913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
40253913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
40353913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
40453913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
40553913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
40653913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
40753913Sarchie
40853913Sarchie/* Get length of an array when the length is stored as a 32 bit
40972645Sasmodai   value immediately preceding the array -- as with struct namelist
41053913Sarchie   and struct typelist. */
41153913Sarchiestatic int
41253913Sarchieng_generic_list_getLength(const struct ng_parse_type *type,
41353913Sarchie	const u_char *start, const u_char *buf)
41453913Sarchie{
41553913Sarchie	return *((const u_int32_t *)(buf - 4));
41653913Sarchie}
41753913Sarchie
41853913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */
41953913Sarchiestatic int
42053913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type,
42153913Sarchie	const u_char *start, const u_char *buf)
42253913Sarchie{
42353913Sarchie	const struct hooklist *hl = (const struct hooklist *)start;
42453913Sarchie
42553913Sarchie	return hl->nodeinfo.hooks;
42653913Sarchie}
42753913Sarchie
42853913Sarchie/* Array type for a variable length array of struct namelist */
42953913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
43053913Sarchie	&ng_generic_nodeinfo_type,
43153913Sarchie	&ng_generic_list_getLength
43253913Sarchie};
43353913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = {
43453913Sarchie	&ng_parse_array_type,
43553913Sarchie	&ng_nodeinfoarray_type_info
43653913Sarchie};
43753913Sarchie
43853913Sarchie/* Array type for a variable length array of struct typelist */
43953913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = {
44053913Sarchie	&ng_generic_typeinfo_type,
44153913Sarchie	&ng_generic_list_getLength
44253913Sarchie};
44353913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = {
44453913Sarchie	&ng_parse_array_type,
44553913Sarchie	&ng_typeinfoarray_type_info
44653913Sarchie};
44753913Sarchie
44853913Sarchie/* Array type for array of struct linkinfo in struct hooklist */
44953913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
45053913Sarchie	&ng_generic_linkinfo_type,
45153913Sarchie	&ng_generic_linkinfo_getLength
45253913Sarchie};
45353913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = {
45453913Sarchie	&ng_parse_array_type,
45553913Sarchie	&ng_generic_linkinfo_array_type_info
45653913Sarchie};
45753913Sarchie
45853913SarchieDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
45953913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
46053913Sarchie	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
46153913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
46253913Sarchie	(&ng_generic_nodeinfoarray_type));
46353913Sarchie
46453913Sarchie/* List of commands and how to convert arguments to/from ASCII */
46553913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = {
46653913Sarchie	{
46753913Sarchie	  NGM_GENERIC_COOKIE,
46853913Sarchie	  NGM_SHUTDOWN,
46953913Sarchie	  "shutdown",
47053913Sarchie	  NULL,
47153913Sarchie	  NULL
47253913Sarchie	},
47353913Sarchie	{
47453913Sarchie	  NGM_GENERIC_COOKIE,
47553913Sarchie	  NGM_MKPEER,
47653913Sarchie	  "mkpeer",
47753913Sarchie	  &ng_generic_mkpeer_type,
47853913Sarchie	  NULL
47953913Sarchie	},
48053913Sarchie	{
48153913Sarchie	  NGM_GENERIC_COOKIE,
48253913Sarchie	  NGM_CONNECT,
48353913Sarchie	  "connect",
48453913Sarchie	  &ng_generic_connect_type,
48553913Sarchie	  NULL
48653913Sarchie	},
48753913Sarchie	{
48853913Sarchie	  NGM_GENERIC_COOKIE,
48953913Sarchie	  NGM_NAME,
49053913Sarchie	  "name",
49153913Sarchie	  &ng_generic_name_type,
49253913Sarchie	  NULL
49353913Sarchie	},
49453913Sarchie	{
49553913Sarchie	  NGM_GENERIC_COOKIE,
49653913Sarchie	  NGM_RMHOOK,
49753913Sarchie	  "rmhook",
49853913Sarchie	  &ng_generic_rmhook_type,
49953913Sarchie	  NULL
50053913Sarchie	},
50153913Sarchie	{
50253913Sarchie	  NGM_GENERIC_COOKIE,
50353913Sarchie	  NGM_NODEINFO,
50453913Sarchie	  "nodeinfo",
50553913Sarchie	  NULL,
50653913Sarchie	  &ng_generic_nodeinfo_type
50753913Sarchie	},
50853913Sarchie	{
50953913Sarchie	  NGM_GENERIC_COOKIE,
51053913Sarchie	  NGM_LISTHOOKS,
51153913Sarchie	  "listhooks",
51253913Sarchie	  NULL,
51353913Sarchie	  &ng_generic_hooklist_type
51453913Sarchie	},
51553913Sarchie	{
51653913Sarchie	  NGM_GENERIC_COOKIE,
51753913Sarchie	  NGM_LISTNAMES,
51853913Sarchie	  "listnames",
51953913Sarchie	  NULL,
52053913Sarchie	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
52153913Sarchie	},
52253913Sarchie	{
52353913Sarchie	  NGM_GENERIC_COOKIE,
52453913Sarchie	  NGM_LISTNODES,
52553913Sarchie	  "listnodes",
52653913Sarchie	  NULL,
52753913Sarchie	  &ng_generic_listnodes_type
52853913Sarchie	},
52953913Sarchie	{
53053913Sarchie	  NGM_GENERIC_COOKIE,
53153913Sarchie	  NGM_LISTTYPES,
53253913Sarchie	  "listtypes",
53353913Sarchie	  NULL,
53453913Sarchie	  &ng_generic_typeinfo_type
53553913Sarchie	},
53653913Sarchie	{
53753913Sarchie	  NGM_GENERIC_COOKIE,
53862471Sphk	  NGM_TEXT_CONFIG,
53962471Sphk	  "textconfig",
54062471Sphk	  NULL,
54162471Sphk	  &ng_parse_string_type
54262471Sphk	},
54362471Sphk	{
54462471Sphk	  NGM_GENERIC_COOKIE,
54553913Sarchie	  NGM_TEXT_STATUS,
54653913Sarchie	  "textstatus",
54753913Sarchie	  NULL,
54853913Sarchie	  &ng_parse_string_type
54953913Sarchie	},
55053913Sarchie	{
55153913Sarchie	  NGM_GENERIC_COOKIE,
55253913Sarchie	  NGM_ASCII2BINARY,
55353913Sarchie	  "ascii2binary",
55453913Sarchie	  &ng_parse_ng_mesg_type,
55553913Sarchie	  &ng_parse_ng_mesg_type
55653913Sarchie	},
55753913Sarchie	{
55853913Sarchie	  NGM_GENERIC_COOKIE,
55953913Sarchie	  NGM_BINARY2ASCII,
56053913Sarchie	  "binary2ascii",
56153913Sarchie	  &ng_parse_ng_mesg_type,
56253913Sarchie	  &ng_parse_ng_mesg_type
56353913Sarchie	},
56453913Sarchie	{ 0 }
56553913Sarchie};
56653913Sarchie
56753913Sarchie/************************************************************************
56852419Sjulian			Node routines
56952419Sjulian************************************************************************/
57052419Sjulian
57152419Sjulian/*
57252419Sjulian * Instantiate a node of the requested type
57352419Sjulian */
57452419Sjulianint
57552419Sjulianng_make_node(const char *typename, node_p *nodepp)
57652419Sjulian{
57752419Sjulian	struct ng_type *type;
57870700Sjulian	int	error;
57952419Sjulian
58052419Sjulian	/* Check that the type makes sense */
58152419Sjulian	if (typename == NULL) {
58271047Sjulian		TRAP_ERROR();
58352419Sjulian		return (EINVAL);
58452419Sjulian	}
58552419Sjulian
586132705Sglebius	/* Locate the node type. If we fail we return. Do not try to load
587132705Sglebius	 * module.
588132705Sglebius	 */
589132705Sglebius	if ((type = ng_findtype(typename)) == NULL)
590132705Sglebius		return (ENXIO);
59152419Sjulian
59270700Sjulian	/*
59370700Sjulian	 * If we have a constructor, then make the node and
59470700Sjulian	 * call the constructor to do type specific initialisation.
59570700Sjulian	 */
59670700Sjulian	if (type->constructor != NULL) {
59770700Sjulian		if ((error = ng_make_node_common(type, nodepp)) == 0) {
598220745Sglebius			if ((error = ((*type->constructor)(*nodepp))) != 0) {
59970784Sjulian				NG_NODE_UNREF(*nodepp);
60070700Sjulian			}
60170700Sjulian		}
60270700Sjulian	} else {
60370700Sjulian		/*
60470700Sjulian		 * Node has no constructor. We cannot ask for one
605167677Srwatson		 * to be made. It must be brought into existence by
60670935Sjulian		 * some external agency. The external agency should
60770700Sjulian		 * call ng_make_node_common() directly to get the
60870700Sjulian		 * netgraph part initialised.
60970700Sjulian		 */
61071047Sjulian		TRAP_ERROR();
61170700Sjulian		error = EINVAL;
61270700Sjulian	}
61370700Sjulian	return (error);
61452419Sjulian}
61552419Sjulian
61652419Sjulian/*
61770700Sjulian * Generic node creation. Called by node initialisation for externally
61870700Sjulian * instantiated nodes (e.g. hardware, sockets, etc ).
61952419Sjulian * The returned node has a reference count of 1.
62052419Sjulian */
62152419Sjulianint
62252419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp)
62352419Sjulian{
62452419Sjulian	node_p node;
62552419Sjulian
62652419Sjulian	/* Require the node type to have been already installed */
62752419Sjulian	if (ng_findtype(type->name) == NULL) {
62871047Sjulian		TRAP_ERROR();
62952419Sjulian		return (EINVAL);
63052419Sjulian	}
63152419Sjulian
63252419Sjulian	/* Make a node and try attach it to the type */
63370784Sjulian	NG_ALLOC_NODE(node);
63452419Sjulian	if (node == NULL) {
63571047Sjulian		TRAP_ERROR();
63652419Sjulian		return (ENOMEM);
63752419Sjulian	}
63870784Sjulian	node->nd_type = type;
639193731Szec#ifdef VIMAGE
640193731Szec	node->nd_vnet = curvnet;
641193731Szec#endif
64270784Sjulian	NG_NODE_REF(node);				/* note reference */
64352419Sjulian	type->refs++;
64452419Sjulian
645168049Swkoszek	NG_QUEUE_LOCK_INIT(&node->nd_input_queue);
646178228Smav	STAILQ_INIT(&node->nd_input_queue.queue);
64770784Sjulian	node->nd_input_queue.q_flags = 0;
64852419Sjulian
64952419Sjulian	/* Initialize hook list for new node */
65070784Sjulian	LIST_INIT(&node->nd_hooks);
65152419Sjulian
652176802Smav	/* Link us into the name hash. */
653176802Smav	mtx_lock(&ng_namehash_mtx);
654181803Sbz	LIST_INSERT_HEAD(&V_ng_name_hash[0], node, nd_nodes);
655176802Smav	mtx_unlock(&ng_namehash_mtx);
65670700Sjulian
65752722Sjulian	/* get an ID and put us in the hash chain */
65872200Sbmilekic	mtx_lock(&ng_idhash_mtx);
65970784Sjulian	for (;;) { /* wrap protection, even if silly */
66070700Sjulian		node_p node2 = NULL;
661181887Sjulian		node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */
66271354Sjulian
66370784Sjulian		/* Is there a problem with the new number? */
66471354Sjulian		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
66571354Sjulian		if ((node->nd_ID != 0) && (node2 == NULL)) {
66670784Sjulian			break;
66770700Sjulian		}
66870784Sjulian	}
669229003Sglebius	LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], node,
670229003Sglebius	    nd_idnodes);
67172200Sbmilekic	mtx_unlock(&ng_idhash_mtx);
67252722Sjulian
67352419Sjulian	/* Done */
67452419Sjulian	*nodepp = node;
67552419Sjulian	return (0);
67652419Sjulian}
67752419Sjulian
67852419Sjulian/*
67952419Sjulian * Forceably start the shutdown process on a node. Either call
680167677Srwatson * its shutdown method, or do the default shutdown if there is
68152419Sjulian * no type-specific method.
68252419Sjulian *
683167677Srwatson * We can only be called from a shutdown message, so we know we have
68470939Sjulian * a writer lock, and therefore exclusive access. It also means
68570939Sjulian * that we should not be on the work queue, but we check anyhow.
68670700Sjulian *
68770700Sjulian * Persistent node types must have a type-specific method which
688167677Srwatson * allocates a new node in which case, this one is irretrievably going away,
68970939Sjulian * or cleans up anything it needs, and just makes the node valid again,
690152451Sglebius * in which case we allow the node to survive.
69170939Sjulian *
692167677Srwatson * XXX We need to think of how to tell a persistent node that we
69370939Sjulian * REALLY need to go away because the hardware has gone or we
69470939Sjulian * are rebooting.... etc.
69552419Sjulian */
69652419Sjulianvoid
69771849Sjulianng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
69852419Sjulian{
69970939Sjulian	hook_p hook;
70070939Sjulian
70152419Sjulian	/* Check if it's already shutting down */
702132464Sjulian	if ((node->nd_flags & NGF_CLOSING) != 0)
70352419Sjulian		return;
70452419Sjulian
70571849Sjulian	if (node == &ng_deadnode) {
70671849Sjulian		printf ("shutdown called on deadnode\n");
70771849Sjulian		return;
70871849Sjulian	}
70971849Sjulian
71052419Sjulian	/* Add an extra reference so it doesn't go away during this */
71170784Sjulian	NG_NODE_REF(node);
71252419Sjulian
71370784Sjulian	/*
71470784Sjulian	 * Mark it invalid so any newcomers know not to try use it
71570784Sjulian	 * Also add our own mark so we can't recurse
716132464Sjulian	 * note that NGF_INVALID does not do this as it's also set during
71770784Sjulian	 * creation
71870784Sjulian	 */
719132464Sjulian	node->nd_flags |= NGF_INVALID|NGF_CLOSING;
72052419Sjulian
721129836Sjulian	/* If node has its pre-shutdown method, then call it first*/
722129836Sjulian	if (node->nd_type && node->nd_type->close)
723129836Sjulian		(*node->nd_type->close)(node);
724129836Sjulian
72570939Sjulian	/* Notify all remaining connected nodes to disconnect */
72670939Sjulian	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
72770939Sjulian		ng_destroy_hook(hook);
72870784Sjulian
72970700Sjulian	/*
73070700Sjulian	 * Drain the input queue forceably.
73170784Sjulian	 * it has no hooks so what's it going to do, bleed on someone?
73270784Sjulian	 * Theoretically we came here from a queue entry that was added
73370784Sjulian	 * Just before the queue was closed, so it should be empty anyway.
73471902Sjulian	 * Also removes us from worklist if needed.
73570700Sjulian	 */
736178228Smav	ng_flush_input_queue(node);
73770700Sjulian
73852419Sjulian	/* Ask the type if it has anything to do in this case */
73970784Sjulian	if (node->nd_type && node->nd_type->shutdown) {
74070784Sjulian		(*node->nd_type->shutdown)(node);
74171849Sjulian		if (NG_NODE_IS_VALID(node)) {
74271849Sjulian			/*
74371849Sjulian			 * Well, blow me down if the node code hasn't declared
74471849Sjulian			 * that it doesn't want to die.
74571849Sjulian			 * Presumably it is a persistant node.
74671849Sjulian			 * If we REALLY want it to go away,
74771849Sjulian			 *  e.g. hardware going away,
748132464Sjulian			 * Our caller should set NGF_REALLY_DIE in nd_flags.
749152451Sglebius			 */
750132464Sjulian			node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
75171849Sjulian			NG_NODE_UNREF(node); /* Assume they still have theirs */
75271849Sjulian			return;
75371849Sjulian		}
75470700Sjulian	} else {				/* do the default thing */
75570784Sjulian		NG_NODE_UNREF(node);
75652419Sjulian	}
75752419Sjulian
75870784Sjulian	ng_unname(node); /* basically a NOP these days */
75970784Sjulian
76070784Sjulian	/*
76170784Sjulian	 * Remove extra reference, possibly the last
76270784Sjulian	 * Possible other holders of references may include
76370784Sjulian	 * timeout callouts, but theoretically the node's supposed to
76470784Sjulian	 * have cancelled them. Possibly hardware dependencies may
76570784Sjulian	 * force a driver to 'linger' with a reference.
76670784Sjulian	 */
76770784Sjulian	NG_NODE_UNREF(node);
76852419Sjulian}
76952419Sjulian
77052419Sjulian/*
77174078Sjulian * Remove a reference to the node, possibly the last.
77274078Sjulian * deadnode always acts as it it were the last.
77352419Sjulian */
774223754Sglebiusvoid
77570784Sjulianng_unref_node(node_p node)
77652419Sjulian{
77771047Sjulian
778223754Sglebius	if (node == &ng_deadnode)
779223754Sglebius		return;
78071047Sjulian
781223754Sglebius	if (refcount_release(&node->nd_refs)) { /* we were the last */
78269519Sjulian
783176802Smav		mtx_lock(&ng_namehash_mtx);
78470784Sjulian		node->nd_type->refs--; /* XXX maybe should get types lock? */
78570784Sjulian		LIST_REMOVE(node, nd_nodes);
786176802Smav		mtx_unlock(&ng_namehash_mtx);
78770700Sjulian
78872200Sbmilekic		mtx_lock(&ng_idhash_mtx);
78970784Sjulian		LIST_REMOVE(node, nd_idnodes);
79072200Sbmilekic		mtx_unlock(&ng_idhash_mtx);
79152419Sjulian
79270791Sjulian		mtx_destroy(&node->nd_input_queue.q_mtx);
79370700Sjulian		NG_FREE_NODE(node);
79452419Sjulian	}
79552419Sjulian}
79652419Sjulian
79752419Sjulian/************************************************************************
79852722Sjulian			Node ID handling
79952722Sjulian************************************************************************/
80052722Sjulianstatic node_p
80170700Sjulianng_ID2noderef(ng_ID_t ID)
80252722Sjulian{
80370784Sjulian	node_p node;
80472200Sbmilekic	mtx_lock(&ng_idhash_mtx);
80571354Sjulian	NG_IDHASH_FIND(ID, node);
80670784Sjulian	if(node)
80770784Sjulian		NG_NODE_REF(node);
80872200Sbmilekic	mtx_unlock(&ng_idhash_mtx);
80970784Sjulian	return(node);
81052722Sjulian}
81152722Sjulian
81252722Sjulianng_ID_t
81352722Sjulianng_node2ID(node_p node)
81452722Sjulian{
81570912Sjulian	return (node ? NG_NODE_ID(node) : 0);
81652722Sjulian}
81752722Sjulian
81852722Sjulian/************************************************************************
81952419Sjulian			Node name handling
82052419Sjulian************************************************************************/
82152419Sjulian
82252419Sjulian/*
823229003Sglebius * Assign a node a name.
82452419Sjulian */
82552419Sjulianint
82652419Sjulianng_name_node(node_p node, const char *name)
82752419Sjulian{
828176802Smav	int i, hash;
82970700Sjulian	node_p node2;
83052419Sjulian
83152419Sjulian	/* Check the name is valid */
832125028Sharti	for (i = 0; i < NG_NODESIZ; i++) {
83352419Sjulian		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
83452419Sjulian			break;
83552419Sjulian	}
83652419Sjulian	if (i == 0 || name[i] != '\0') {
83771047Sjulian		TRAP_ERROR();
83852419Sjulian		return (EINVAL);
83952419Sjulian	}
84052722Sjulian	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
84171047Sjulian		TRAP_ERROR();
84252419Sjulian		return (EINVAL);
84352419Sjulian	}
84452419Sjulian
84552419Sjulian	/* Check the name isn't already being used */
84670700Sjulian	if ((node2 = ng_name2noderef(node, name)) != NULL) {
84770784Sjulian		NG_NODE_UNREF(node2);
84871047Sjulian		TRAP_ERROR();
84952419Sjulian		return (EADDRINUSE);
85052419Sjulian	}
85152419Sjulian
85270700Sjulian	/* copy it */
853125028Sharti	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
85452419Sjulian
855176802Smav	/* Update name hash. */
856176802Smav	NG_NAMEHASH(name, hash);
857176802Smav	mtx_lock(&ng_namehash_mtx);
858176802Smav	LIST_REMOVE(node, nd_nodes);
859181803Sbz	LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes);
860176802Smav	mtx_unlock(&ng_namehash_mtx);
861176802Smav
86252419Sjulian	return (0);
86352419Sjulian}
86452419Sjulian
86552419Sjulian/*
86652419Sjulian * Find a node by absolute name. The name should NOT end with ':'
86752419Sjulian * The name "." means "this node" and "[xxx]" means "the node
86852419Sjulian * with ID (ie, at address) xxx".
86952419Sjulian *
87052419Sjulian * Returns the node if found, else NULL.
87170700Sjulian * Eventually should add something faster than a sequential search.
872170035Srwatson * Note it acquires a reference on the node so you can be sure it's still
873170035Srwatson * there.
87452419Sjulian */
87552419Sjuliannode_p
87670700Sjulianng_name2noderef(node_p here, const char *name)
87752419Sjulian{
87852722Sjulian	node_p node;
87952722Sjulian	ng_ID_t temp;
880176802Smav	int	hash;
88152419Sjulian
88252419Sjulian	/* "." means "this node" */
88370700Sjulian	if (strcmp(name, ".") == 0) {
88470784Sjulian		NG_NODE_REF(here);
88570700Sjulian		return(here);
88670700Sjulian	}
88752419Sjulian
88852419Sjulian	/* Check for name-by-ID */
88952722Sjulian	if ((temp = ng_decodeidname(name)) != 0) {
89070700Sjulian		return (ng_ID2noderef(temp));
89152419Sjulian	}
89252419Sjulian
89352419Sjulian	/* Find node by name */
894176802Smav	NG_NAMEHASH(name, hash);
895176802Smav	mtx_lock(&ng_namehash_mtx);
896181803Sbz	LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes) {
897176802Smav		if (NG_NODE_IS_VALID(node) &&
898176802Smav		    (strcmp(NG_NODE_NAME(node), name) == 0)) {
89952419Sjulian			break;
90070912Sjulian		}
90152419Sjulian	}
90270700Sjulian	if (node)
90370784Sjulian		NG_NODE_REF(node);
904176802Smav	mtx_unlock(&ng_namehash_mtx);
90552419Sjulian	return (node);
90652419Sjulian}
90752419Sjulian
90852419Sjulian/*
909108533Sschweikh * Decode an ID name, eg. "[f03034de]". Returns 0 if the
91052722Sjulian * string is not valid, otherwise returns the value.
91152419Sjulian */
91252722Sjulianstatic ng_ID_t
91352419Sjulianng_decodeidname(const char *name)
91452419Sjulian{
91552816Sarchie	const int len = strlen(name);
91653648Sarchie	char *eptr;
91752816Sarchie	u_long val;
91852419Sjulian
91952816Sarchie	/* Check for proper length, brackets, no leading junk */
920229003Sglebius	if ((len < 3) || (name[0] != '[') || (name[len - 1] != ']') ||
921229003Sglebius	    (!isxdigit(name[1])))
92270912Sjulian		return ((ng_ID_t)0);
92352419Sjulian
92452816Sarchie	/* Decode number */
92552816Sarchie	val = strtoul(name + 1, &eptr, 16);
926229003Sglebius	if ((eptr - name != len - 1) || (val == ULONG_MAX) || (val == 0))
92753042Sjulian		return ((ng_ID_t)0);
928229003Sglebius
929229003Sglebius	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.
93552419Sjulian */
93652419Sjulianvoid
93752419Sjulianng_unname(node_p node)
93852419Sjulian{
93952419Sjulian}
94052419Sjulian
94152419Sjulian/************************************************************************
94252419Sjulian			Hook routines
94352419Sjulian Names are not optional. Hooks are always connected, except for a
94470939Sjulian brief moment within these routines. On invalidation or during creation
94570939Sjulian they are connected to the 'dead' hook.
94652419Sjulian************************************************************************/
94752419Sjulian
94852419Sjulian/*
94952419Sjulian * Remove a hook reference
95052419Sjulian */
95170784Sjulianvoid
95252419Sjulianng_unref_hook(hook_p hook)
95352419Sjulian{
95471047Sjulian
955223754Sglebius	if (hook == &ng_deadhook)
95671047Sjulian		return;
95769519Sjulian
958223754Sglebius	if (refcount_release(&hook->hk_refs)) { /* we were the last */
959177722Smav		if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
96071047Sjulian			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
96170700Sjulian		NG_FREE_HOOK(hook);
96270700Sjulian	}
96352419Sjulian}
96452419Sjulian
96552419Sjulian/*
96652419Sjulian * Add an unconnected hook to a node. Only used internally.
96770939Sjulian * Assumes node is locked. (XXX not yet true )
96852419Sjulian */
96952419Sjulianstatic int
97052419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp)
97152419Sjulian{
97252419Sjulian	hook_p hook;
97352419Sjulian	int error = 0;
97452419Sjulian
97552419Sjulian	/* Check that the given name is good */
97652419Sjulian	if (name == NULL) {
97771047Sjulian		TRAP_ERROR();
97852419Sjulian		return (EINVAL);
97952419Sjulian	}
98054096Sarchie	if (ng_findhook(node, name) != NULL) {
98171047Sjulian		TRAP_ERROR();
98254096Sarchie		return (EEXIST);
98352419Sjulian	}
98452419Sjulian
98552419Sjulian	/* Allocate the hook and link it up */
98670784Sjulian	NG_ALLOC_HOOK(hook);
98752419Sjulian	if (hook == NULL) {
98871047Sjulian		TRAP_ERROR();
98952419Sjulian		return (ENOMEM);
99052419Sjulian	}
99170939Sjulian	hook->hk_refs = 1;		/* add a reference for us to return */
99270784Sjulian	hook->hk_flags = HK_INVALID;
99370939Sjulian	hook->hk_peer = &ng_deadhook;	/* start off this way */
99470784Sjulian	hook->hk_node = node;
99570784Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
99652419Sjulian
99770939Sjulian	/* Set hook name */
998125028Sharti	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
99970939Sjulian
100070939Sjulian	/*
100170939Sjulian	 * Check if the node type code has something to say about it
100270939Sjulian	 * If it fails, the unref of the hook will also unref the node.
100370939Sjulian	 */
100470935Sjulian	if (node->nd_type->newhook != NULL) {
100570935Sjulian		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
100670784Sjulian			NG_HOOK_UNREF(hook);	/* this frees the hook */
100770700Sjulian			return (error);
100870700Sjulian		}
100970935Sjulian	}
101052419Sjulian	/*
101152419Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
101252419Sjulian	 * We'll ask again later when we actually connect the hooks.
101352419Sjulian	 */
101470784Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
101570784Sjulian	node->nd_numhooks++;
101670939Sjulian	NG_HOOK_REF(hook);	/* one for the node */
101752419Sjulian
101852419Sjulian	if (hookp)
101952419Sjulian		*hookp = hook;
102070939Sjulian	return (0);
102152419Sjulian}
102252419Sjulian
102352419Sjulian/*
102454096Sarchie * Find a hook
102554096Sarchie *
102654096Sarchie * Node types may supply their own optimized routines for finding
102754096Sarchie * hooks.  If none is supplied, we just do a linear search.
102870939Sjulian * XXX Possibly we should add a reference to the hook?
102954096Sarchie */
103054096Sarchiehook_p
103154096Sarchieng_findhook(node_p node, const char *name)
103254096Sarchie{
103354096Sarchie	hook_p hook;
103454096Sarchie
103570784Sjulian	if (node->nd_type->findhook != NULL)
103670784Sjulian		return (*node->nd_type->findhook)(node, name);
103770784Sjulian	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
1038229003Sglebius		if (NG_HOOK_IS_VALID(hook) &&
1039229003Sglebius		    (strcmp(NG_HOOK_NAME(hook), name) == 0))
104054096Sarchie			return (hook);
104154096Sarchie	}
104254096Sarchie	return (NULL);
104354096Sarchie}
104454096Sarchie
104554096Sarchie/*
104652419Sjulian * Destroy a hook
104752419Sjulian *
104852419Sjulian * As hooks are always attached, this really destroys two hooks.
104952419Sjulian * The one given, and the one attached to it. Disconnect the hooks
105070939Sjulian * from each other first. We reconnect the peer hook to the 'dead'
105170939Sjulian * hook so that it can still exist after we depart. We then
105270939Sjulian * send the peer its own destroy message. This ensures that we only
1053152451Sglebius * interact with the peer's structures when it is locked processing that
105470939Sjulian * message. We hold a reference to the peer hook so we are guaranteed that
105570939Sjulian * the peer hook and node are still going to exist until
105670939Sjulian * we are finished there as the hook holds a ref on the node.
1057152451Sglebius * We run this same code again on the peer hook, but that time it is already
1058152451Sglebius * attached to the 'dead' hook.
105971047Sjulian *
1060152451Sglebius * This routine is called at all stages of hook creation
106171047Sjulian * on error detection and must be able to handle any such stage.
106252419Sjulian */
106352419Sjulianvoid
106452419Sjulianng_destroy_hook(hook_p hook)
106552419Sjulian{
1066151974Sglebius	hook_p peer;
1067151974Sglebius	node_p node;
106852419Sjulian
106971047Sjulian	if (hook == &ng_deadhook) {	/* better safe than sorry */
107071047Sjulian		printf("ng_destroy_hook called on deadhook\n");
107171047Sjulian		return;
107271047Sjulian	}
1073151974Sglebius
1074151974Sglebius	/*
1075151974Sglebius	 * Protect divorce process with mutex, to avoid races on
1076151974Sglebius	 * simultaneous disconnect.
1077151974Sglebius	 */
1078151974Sglebius	mtx_lock(&ng_topo_mtx);
1079151974Sglebius
1080151974Sglebius	hook->hk_flags |= HK_INVALID;
1081151974Sglebius
1082151974Sglebius	peer = NG_HOOK_PEER(hook);
1083151974Sglebius	node = NG_HOOK_NODE(hook);
1084151974Sglebius
108570939Sjulian	if (peer && (peer != &ng_deadhook)) {
108670939Sjulian		/*
108770939Sjulian		 * Set the peer to point to ng_deadhook
108870939Sjulian		 * from this moment on we are effectively independent it.
108970939Sjulian		 * send it an rmhook message of it's own.
109070939Sjulian		 */
109170939Sjulian		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
109270939Sjulian		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
109371047Sjulian		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
1094152451Sglebius			/*
109571047Sjulian			 * If it's already divorced from a node,
109671047Sjulian			 * just free it.
109771047Sjulian			 */
1098151974Sglebius			mtx_unlock(&ng_topo_mtx);
109971047Sjulian		} else {
1100151974Sglebius			mtx_unlock(&ng_topo_mtx);
110171047Sjulian			ng_rmhook_self(peer); 	/* Send it a surprise */
110271047Sjulian		}
110370942Sjulian		NG_HOOK_UNREF(peer);		/* account for peer link */
110470942Sjulian		NG_HOOK_UNREF(hook);		/* account for peer link */
1105151974Sglebius	} else
1106151974Sglebius		mtx_unlock(&ng_topo_mtx);
110752419Sjulian
1108151974Sglebius	mtx_assert(&ng_topo_mtx, MA_NOTOWNED);
1109151974Sglebius
111052419Sjulian	/*
111152419Sjulian	 * Remove the hook from the node's list to avoid possible recursion
111252419Sjulian	 * in case the disconnection results in node shutdown.
111352419Sjulian	 */
111471047Sjulian	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
111571047Sjulian		return;
111671047Sjulian	}
111770784Sjulian	LIST_REMOVE(hook, hk_hooks);
111870784Sjulian	node->nd_numhooks--;
111970784Sjulian	if (node->nd_type->disconnect) {
112052419Sjulian		/*
112171047Sjulian		 * The type handler may elect to destroy the node so don't
1122167677Srwatson		 * trust its existence after this point. (except
112371047Sjulian		 * that we still hold a reference on it. (which we
112471047Sjulian		 * inherrited from the hook we are destroying)
112552419Sjulian		 */
112670784Sjulian		(*node->nd_type->disconnect) (hook);
112752419Sjulian	}
112871047Sjulian
112971047Sjulian	/*
113071047Sjulian	 * Note that because we will point to ng_deadnode, the original node
113171047Sjulian	 * is not decremented automatically so we do that manually.
113271047Sjulian	 */
113371047Sjulian	_NG_HOOK_NODE(hook) = &ng_deadnode;
113471047Sjulian	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
113571047Sjulian	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
113652419Sjulian}
113752419Sjulian
113852419Sjulian/*
113952419Sjulian * Take two hooks on a node and merge the connection so that the given node
114052419Sjulian * is effectively bypassed.
114152419Sjulian */
114252419Sjulianint
114352419Sjulianng_bypass(hook_p hook1, hook_p hook2)
114452419Sjulian{
114570784Sjulian	if (hook1->hk_node != hook2->hk_node) {
114671047Sjulian		TRAP_ERROR();
114752419Sjulian		return (EINVAL);
114870784Sjulian	}
1149219827Sglebius	mtx_lock(&ng_topo_mtx);
115070784Sjulian	hook1->hk_peer->hk_peer = hook2->hk_peer;
115170784Sjulian	hook2->hk_peer->hk_peer = hook1->hk_peer;
115252419Sjulian
115370939Sjulian	hook1->hk_peer = &ng_deadhook;
115470939Sjulian	hook2->hk_peer = &ng_deadhook;
1155219827Sglebius	mtx_unlock(&ng_topo_mtx);
115670939Sjulian
1157163244Sglebius	NG_HOOK_UNREF(hook1);
1158163244Sglebius	NG_HOOK_UNREF(hook2);
1159163244Sglebius
116052419Sjulian	/* XXX If we ever cache methods on hooks update them as well */
116152419Sjulian	ng_destroy_hook(hook1);
116252419Sjulian	ng_destroy_hook(hook2);
116352419Sjulian	return (0);
116452419Sjulian}
116552419Sjulian
116652419Sjulian/*
116752419Sjulian * Install a new netgraph type
116852419Sjulian */
116952419Sjulianint
117052419Sjulianng_newtype(struct ng_type *tp)
117152419Sjulian{
117252419Sjulian	const size_t namelen = strlen(tp->name);
117352419Sjulian
117452419Sjulian	/* Check version and type name fields */
1175229003Sglebius	if ((tp->version != NG_ABI_VERSION) || (namelen == 0) ||
1176229003Sglebius	    (namelen >= NG_TYPESIZ)) {
117771047Sjulian		TRAP_ERROR();
1178131374Sjulian		if (tp->version != NG_ABI_VERSION) {
1179229003Sglebius			printf("Netgraph: Node type rejected. ABI mismatch. "
1180229003Sglebius			    "Suggest recompile\n");
1181131374Sjulian		}
118252419Sjulian		return (EINVAL);
118352419Sjulian	}
118452419Sjulian
118552419Sjulian	/* Check for name collision */
118652419Sjulian	if (ng_findtype(tp->name) != NULL) {
118771047Sjulian		TRAP_ERROR();
118852419Sjulian		return (EEXIST);
118952419Sjulian	}
119052419Sjulian
119170700Sjulian
119252419Sjulian	/* Link in new type */
119372200Sbmilekic	mtx_lock(&ng_typelist_mtx);
119470700Sjulian	LIST_INSERT_HEAD(&ng_typelist, tp, types);
119571603Sjulian	tp->refs = 1;	/* first ref is linked list */
119672200Sbmilekic	mtx_unlock(&ng_typelist_mtx);
119752419Sjulian	return (0);
119852419Sjulian}
119952419Sjulian
120052419Sjulian/*
120180222Sjulian * unlink a netgraph type
120280222Sjulian * If no examples exist
120380222Sjulian */
120480222Sjulianint
120580222Sjulianng_rmtype(struct ng_type *tp)
120680222Sjulian{
120780222Sjulian	/* Check for name collision */
120880222Sjulian	if (tp->refs != 1) {
120980222Sjulian		TRAP_ERROR();
121080222Sjulian		return (EBUSY);
121180222Sjulian	}
121280222Sjulian
121380222Sjulian	/* Unlink type */
121480222Sjulian	mtx_lock(&ng_typelist_mtx);
121580222Sjulian	LIST_REMOVE(tp, types);
121680222Sjulian	mtx_unlock(&ng_typelist_mtx);
121780222Sjulian	return (0);
121880222Sjulian}
121980222Sjulian
122080222Sjulian/*
122152419Sjulian * Look for a type of the name given
122252419Sjulian */
122352419Sjulianstruct ng_type *
122452419Sjulianng_findtype(const char *typename)
122552419Sjulian{
122652419Sjulian	struct ng_type *type;
122752419Sjulian
122872200Sbmilekic	mtx_lock(&ng_typelist_mtx);
122970700Sjulian	LIST_FOREACH(type, &ng_typelist, types) {
123052419Sjulian		if (strcmp(type->name, typename) == 0)
123152419Sjulian			break;
123252419Sjulian	}
123372200Sbmilekic	mtx_unlock(&ng_typelist_mtx);
123452419Sjulian	return (type);
123552419Sjulian}
123652419Sjulian
123752419Sjulian/************************************************************************
123852419Sjulian			Composite routines
123952419Sjulian************************************************************************/
124052419Sjulian/*
124171047Sjulian * Connect two nodes using the specified hooks, using queued functions.
124252419Sjulian */
1243172806Smavstatic int
1244172806Smavng_con_part3(node_p node, item_p item, hook_p hook)
124552419Sjulian{
1246172806Smav	int	error = 0;
124752419Sjulian
124871047Sjulian	/*
124971047Sjulian	 * When we run, we know that the node 'node' is locked for us.
125071047Sjulian	 * Our caller has a reference on the hook.
125171047Sjulian	 * Our caller has a reference on the node.
125271047Sjulian	 * (In this case our caller is ng_apply_item() ).
125371047Sjulian	 * The peer hook has a reference on the hook.
125471849Sjulian	 * We are all set up except for the final call to the node, and
125571849Sjulian	 * the clearing of the INVALID flag.
125671047Sjulian	 */
125771047Sjulian	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
125871047Sjulian		/*
125971047Sjulian		 * The node must have been freed again since we last visited
126071047Sjulian		 * here. ng_destry_hook() has this effect but nothing else does.
126171047Sjulian		 * We should just release our references and
126271047Sjulian		 * free anything we can think of.
126371047Sjulian		 * Since we know it's been destroyed, and it's our caller
126471047Sjulian		 * that holds the references, just return.
126571047Sjulian		 */
1266172806Smav		ERROUT(ENOENT);
126752419Sjulian	}
126871047Sjulian	if (hook->hk_node->nd_type->connect) {
1269172806Smav		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
127071047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
127171849Sjulian			printf("failed in ng_con_part3()\n");
1272172806Smav			ERROUT(error);
127371047Sjulian		}
127452419Sjulian	}
127571047Sjulian	/*
127671047Sjulian	 *  XXX this is wrong for SMP. Possibly we need
127771047Sjulian	 * to separate out 'create' and 'invalid' flags.
127871047Sjulian	 * should only set flags on hooks we have locked under our node.
127971047Sjulian	 */
128071047Sjulian	hook->hk_flags &= ~HK_INVALID;
1281172806Smavdone:
1282172806Smav	NG_FREE_ITEM(item);
1283172806Smav	return (error);
128471047Sjulian}
128552419Sjulian
1286172806Smavstatic int
1287172806Smavng_con_part2(node_p node, item_p item, hook_p hook)
128871047Sjulian{
1289172806Smav	hook_p	peer;
1290172806Smav	int	error = 0;
129171047Sjulian
129252419Sjulian	/*
129371047Sjulian	 * When we run, we know that the node 'node' is locked for us.
129471047Sjulian	 * Our caller has a reference on the hook.
129571047Sjulian	 * Our caller has a reference on the node.
129671047Sjulian	 * (In this case our caller is ng_apply_item() ).
129771047Sjulian	 * The peer hook has a reference on the hook.
129871047Sjulian	 * our node pointer points to the 'dead' node.
129971047Sjulian	 * First check the hook name is unique.
130071849Sjulian	 * Should not happen because we checked before queueing this.
130152419Sjulian	 */
130271047Sjulian	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
130371047Sjulian		TRAP_ERROR();
130471047Sjulian		ng_destroy_hook(hook); /* should destroy peer too */
130571849Sjulian		printf("failed in ng_con_part2()\n");
1306172806Smav		ERROUT(EEXIST);
130771047Sjulian	}
130870939Sjulian	/*
130971047Sjulian	 * Check if the node type code has something to say about it
131071047Sjulian	 * If it fails, the unref of the hook will also unref the attached node,
131171047Sjulian	 * however since that node is 'ng_deadnode' this will do nothing.
131271047Sjulian	 * The peer hook will also be destroyed.
131370939Sjulian	 */
131471047Sjulian	if (node->nd_type->newhook != NULL) {
1315172806Smav		if ((error = (*node->nd_type->newhook)(node, hook,
1316172806Smav		    hook->hk_name))) {
131771047Sjulian			ng_destroy_hook(hook); /* should destroy peer too */
131871849Sjulian			printf("failed in ng_con_part2()\n");
1319172806Smav			ERROUT(error);
132071047Sjulian		}
132171047Sjulian	}
132271047Sjulian
132371047Sjulian	/*
132471047Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
132571047Sjulian	 * We'll ask again later when we actually connect the hooks.
132671047Sjulian	 */
132771047Sjulian	hook->hk_node = node;		/* just overwrite ng_deadnode */
132871047Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
132971047Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
133071047Sjulian	node->nd_numhooks++;
133171047Sjulian	NG_HOOK_REF(hook);	/* one for the node */
133271047Sjulian
133371047Sjulian	/*
1334167677Srwatson	 * We now have a symmetrical situation, where both hooks have been
133574078Sjulian	 * linked to their nodes, the newhook methods have been called
133671047Sjulian	 * And the references are all correct. The hooks are still marked
133771047Sjulian	 * as invalid, as we have not called the 'connect' methods
133871047Sjulian	 * yet.
1339167677Srwatson	 * We can call the local one immediately as we have the
134071047Sjulian	 * node locked, but we need to queue the remote one.
134171047Sjulian	 */
134271047Sjulian	if (hook->hk_node->nd_type->connect) {
1343172806Smav		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
134471047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
134571849Sjulian			printf("failed in ng_con_part2(A)\n");
1346172806Smav			ERROUT(error);
134771047Sjulian		}
134871047Sjulian	}
1349151974Sglebius
1350151974Sglebius	/*
1351151974Sglebius	 * Acquire topo mutex to avoid race with ng_destroy_hook().
1352151974Sglebius	 */
1353151974Sglebius	mtx_lock(&ng_topo_mtx);
1354151974Sglebius	peer = hook->hk_peer;
1355151974Sglebius	if (peer == &ng_deadhook) {
1356151974Sglebius		mtx_unlock(&ng_topo_mtx);
1357151974Sglebius		printf("failed in ng_con_part2(B)\n");
1358151974Sglebius		ng_destroy_hook(hook);
1359172806Smav		ERROUT(ENOENT);
1360151974Sglebius	}
1361151974Sglebius	mtx_unlock(&ng_topo_mtx);
1362151974Sglebius
1363173605Sglebius	if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3,
1364173605Sglebius	    NULL, 0, NG_REUSE_ITEM))) {
1365151974Sglebius		printf("failed in ng_con_part2(C)\n");
136671849Sjulian		ng_destroy_hook(hook);	/* also zaps peer */
1367172806Smav		return (error);		/* item was consumed. */
136871849Sjulian	}
136971047Sjulian	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
1370172806Smav	return (0);			/* item was consumed. */
1371172806Smavdone:
1372172806Smav	NG_FREE_ITEM(item);
1373172806Smav	return (error);
137452419Sjulian}
137552419Sjulian
137652419Sjulian/*
1377152451Sglebius * Connect this node with another node. We assume that this node is
137871047Sjulian * currently locked, as we are only called from an NGM_CONNECT message.
137952419Sjulian */
138071047Sjulianstatic int
1381172806Smavng_con_nodes(item_p item, node_p node, const char *name,
1382172806Smav    node_p node2, const char *name2)
138352419Sjulian{
1384152451Sglebius	int	error;
1385152451Sglebius	hook_p	hook;
1386152451Sglebius	hook_p	hook2;
138752419Sjulian
138871849Sjulian	if (ng_findhook(node2, name2) != NULL) {
138971849Sjulian		return(EEXIST);
139071849Sjulian	}
139170939Sjulian	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
139252419Sjulian		return (error);
139371047Sjulian	/* Allocate the other hook and link it up */
139471047Sjulian	NG_ALLOC_HOOK(hook2);
1395140737Sglebius	if (hook2 == NULL) {
139671047Sjulian		TRAP_ERROR();
139771047Sjulian		ng_destroy_hook(hook);	/* XXX check ref counts so far */
139871047Sjulian		NG_HOOK_UNREF(hook);	/* including our ref */
139971047Sjulian		return (ENOMEM);
140071047Sjulian	}
140171047Sjulian	hook2->hk_refs = 1;		/* start with a reference for us. */
140271047Sjulian	hook2->hk_flags = HK_INVALID;
140371047Sjulian	hook2->hk_peer = hook;		/* Link the two together */
140471047Sjulian	hook->hk_peer = hook2;
140571047Sjulian	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
140671047Sjulian	NG_HOOK_REF(hook2);
1407152451Sglebius	hook2->hk_node = &ng_deadnode;
1408125028Sharti	strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
140971047Sjulian
141071047Sjulian	/*
141171047Sjulian	 * Queue the function above.
141271047Sjulian	 * Procesing continues in that function in the lock context of
141371047Sjulian	 * the other node.
141471047Sjulian	 */
1415173605Sglebius	if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0,
1416173605Sglebius	    NG_NOFLAGS))) {
1417171885Smav		printf("failed in ng_con_nodes(): %d\n", error);
1418171885Smav		ng_destroy_hook(hook);	/* also zaps peer */
1419171885Smav	}
142071047Sjulian
142171047Sjulian	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
142271047Sjulian	NG_HOOK_UNREF(hook2);
1423171885Smav	return (error);
142471047Sjulian}
142571047Sjulian
142671047Sjulian/*
142771047Sjulian * Make a peer and connect.
142871047Sjulian * We assume that the local node is locked.
142971047Sjulian * The new node probably doesn't need a lock until
143071047Sjulian * it has a hook, because it cannot really have any work until then,
143171047Sjulian * but we should think about it a bit more.
143271047Sjulian *
143371047Sjulian * The problem may come if the other node also fires up
143471047Sjulian * some hardware or a timer or some other source of activation,
143571047Sjulian * also it may already get a command msg via it's ID.
143671047Sjulian *
143771047Sjulian * We could use the same method as ng_con_nodes() but we'd have
1438152451Sglebius * to add ability to remove the node when failing. (Not hard, just
143971047Sjulian * make arg1 point to the node to remove).
144071047Sjulian * Unless of course we just ignore failure to connect and leave
144171047Sjulian * an unconnected node?
144271047Sjulian */
144371047Sjulianstatic int
144471047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type)
144571047Sjulian{
1446152451Sglebius	node_p	node2;
1447152451Sglebius	hook_p	hook1, hook2;
1448152451Sglebius	int	error;
144971047Sjulian
145071047Sjulian	if ((error = ng_make_node(type, &node2))) {
145152419Sjulian		return (error);
145252419Sjulian	}
145370939Sjulian
145471047Sjulian	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
145571849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
145671047Sjulian		return (error);
145771047Sjulian	}
145871047Sjulian
145971047Sjulian	if ((error = ng_add_hook(node2, name2, &hook2))) {
146071849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
146171047Sjulian		ng_destroy_hook(hook1);
146271047Sjulian		NG_HOOK_UNREF(hook1);
146371047Sjulian		return (error);
146471047Sjulian	}
146571047Sjulian
146670939Sjulian	/*
146771047Sjulian	 * Actually link the two hooks together.
146871047Sjulian	 */
146971047Sjulian	hook1->hk_peer = hook2;
147071047Sjulian	hook2->hk_peer = hook1;
147171047Sjulian
147271047Sjulian	/* Each hook is referenced by the other */
147371047Sjulian	NG_HOOK_REF(hook1);
147471047Sjulian	NG_HOOK_REF(hook2);
147571047Sjulian
147671047Sjulian	/* Give each node the opportunity to veto the pending connection */
147771047Sjulian	if (hook1->hk_node->nd_type->connect) {
147871047Sjulian		error = (*hook1->hk_node->nd_type->connect) (hook1);
147971047Sjulian	}
148071047Sjulian
148171047Sjulian	if ((error == 0) && hook2->hk_node->nd_type->connect) {
148271047Sjulian		error = (*hook2->hk_node->nd_type->connect) (hook2);
148371047Sjulian
148471047Sjulian	}
148571047Sjulian
148671047Sjulian	/*
148770939Sjulian	 * drop the references we were holding on the two hooks.
148870939Sjulian	 */
148971047Sjulian	if (error) {
149071047Sjulian		ng_destroy_hook(hook2);	/* also zaps hook1 */
149171849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
149271047Sjulian	} else {
149371047Sjulian		/* As a last act, allow the hooks to be used */
149471047Sjulian		hook1->hk_flags &= ~HK_INVALID;
149571047Sjulian		hook2->hk_flags &= ~HK_INVALID;
149671047Sjulian	}
149771047Sjulian	NG_HOOK_UNREF(hook1);
149870939Sjulian	NG_HOOK_UNREF(hook2);
149970939Sjulian	return (error);
150052419Sjulian}
150171047Sjulian
150270700Sjulian/************************************************************************
150370700Sjulian		Utility routines to send self messages
150470700Sjulian************************************************************************/
150570700Sjulian
150671849Sjulian/* Shut this node down as soon as everyone is clear of it */
1507167677Srwatson/* Should add arg "immediately" to jump the queue */
150870700Sjulianint
1509186060Smavng_rmnode_self(node_p node)
151070700Sjulian{
151171849Sjulian	int		error;
151252419Sjulian
151371849Sjulian	if (node == &ng_deadnode)
151471849Sjulian		return (0);
1515132464Sjulian	node->nd_flags |= NGF_INVALID;
1516132464Sjulian	if (node->nd_flags & NGF_CLOSING)
151771849Sjulian		return (0);
151870700Sjulian
1519186060Smav	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
152071849Sjulian	return (error);
152170700Sjulian}
152270700Sjulian
152371849Sjulianstatic void
152471047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
152571047Sjulian{
152671047Sjulian	ng_destroy_hook(hook);
152771849Sjulian	return ;
152871047Sjulian}
152971047Sjulian
153070935Sjulianint
153170935Sjulianng_rmhook_self(hook_p hook)
153270935Sjulian{
153371047Sjulian	int		error;
153470935Sjulian	node_p node = NG_HOOK_NODE(hook);
153570935Sjulian
153671047Sjulian	if (node == &ng_deadnode)
153771047Sjulian		return (0);
153871047Sjulian
153971047Sjulian	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
154071047Sjulian	return (error);
154170935Sjulian}
154270935Sjulian
154370700Sjulian/***********************************************************************
154452419Sjulian * Parse and verify a string of the form:  <NODE:><PATH>
154552419Sjulian *
154652419Sjulian * Such a string can refer to a specific node or a specific hook
154752419Sjulian * on a specific node, depending on how you look at it. In the
154852419Sjulian * latter case, the PATH component must not end in a dot.
154952419Sjulian *
155052419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string
155152419Sjulian * of hook names separated by dots. This breaks out the original
155252419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp
155352419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to
155452419Sjulian * the final hook component of <PATH>, if any, otherwise NULL.
155552419Sjulian *
155652419Sjulian * This returns -1 if the path is malformed. The char ** are optional.
155770700Sjulian ***********************************************************************/
155852419Sjulianint
155952419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
156052419Sjulian{
1561152451Sglebius	char	*node, *path, *hook;
1562152451Sglebius	int	k;
156352419Sjulian
156452419Sjulian	/*
156552419Sjulian	 * Extract absolute NODE, if any
156652419Sjulian	 */
156752419Sjulian	for (path = addr; *path && *path != ':'; path++);
156852419Sjulian	if (*path) {
156952419Sjulian		node = addr;	/* Here's the NODE */
157052419Sjulian		*path++ = '\0';	/* Here's the PATH */
157152419Sjulian
157252419Sjulian		/* Node name must not be empty */
157352419Sjulian		if (!*node)
157452419Sjulian			return -1;
157552419Sjulian
157652419Sjulian		/* A name of "." is OK; otherwise '.' not allowed */
157752419Sjulian		if (strcmp(node, ".") != 0) {
157852419Sjulian			for (k = 0; node[k]; k++)
157952419Sjulian				if (node[k] == '.')
158052419Sjulian					return -1;
158152419Sjulian		}
158252419Sjulian	} else {
158352419Sjulian		node = NULL;	/* No absolute NODE */
158452419Sjulian		path = addr;	/* Here's the PATH */
158552419Sjulian	}
158652419Sjulian
158752419Sjulian	/* Snoop for illegal characters in PATH */
158852419Sjulian	for (k = 0; path[k]; k++)
158952419Sjulian		if (path[k] == ':')
159052419Sjulian			return -1;
159152419Sjulian
159252419Sjulian	/* Check for no repeated dots in PATH */
159352419Sjulian	for (k = 0; path[k]; k++)
159452419Sjulian		if (path[k] == '.' && path[k + 1] == '.')
159552419Sjulian			return -1;
159652419Sjulian
159752419Sjulian	/* Remove extra (degenerate) dots from beginning or end of PATH */
159852419Sjulian	if (path[0] == '.')
159952419Sjulian		path++;
160052419Sjulian	if (*path && path[strlen(path) - 1] == '.')
160152419Sjulian		path[strlen(path) - 1] = 0;
160252419Sjulian
160352419Sjulian	/* If PATH has a dot, then we're not talking about a hook */
160452419Sjulian	if (*path) {
160552419Sjulian		for (hook = path, k = 0; path[k]; k++)
160652419Sjulian			if (path[k] == '.') {
160752419Sjulian				hook = NULL;
160852419Sjulian				break;
160952419Sjulian			}
161052419Sjulian	} else
161152419Sjulian		path = hook = NULL;
161252419Sjulian
161352419Sjulian	/* Done */
161452419Sjulian	if (nodep)
161552419Sjulian		*nodep = node;
161652419Sjulian	if (pathp)
161752419Sjulian		*pathp = path;
161852419Sjulian	if (hookp)
161952419Sjulian		*hookp = hook;
162052419Sjulian	return (0);
162152419Sjulian}
162252419Sjulian
162352419Sjulian/*
162452419Sjulian * Given a path, which may be absolute or relative, and a starting node,
162570700Sjulian * return the destination node.
162652419Sjulian */
162752419Sjulianint
1628229003Sglebiusng_path2noderef(node_p here, const char *address, node_p *destp,
1629229003Sglebius    hook_p *lasthook)
163052419Sjulian{
1631125028Sharti	char    fullpath[NG_PATHSIZ];
1632219827Sglebius	char   *nodename, *path;
163370700Sjulian	node_p  node, oldnode;
163452419Sjulian
163552419Sjulian	/* Initialize */
163670784Sjulian	if (destp == NULL) {
163771047Sjulian		TRAP_ERROR();
163852419Sjulian		return EINVAL;
163970784Sjulian	}
164052419Sjulian	*destp = NULL;
164152419Sjulian
164252419Sjulian	/* Make a writable copy of address for ng_path_parse() */
164352419Sjulian	strncpy(fullpath, address, sizeof(fullpath) - 1);
164452419Sjulian	fullpath[sizeof(fullpath) - 1] = '\0';
164552419Sjulian
164652419Sjulian	/* Parse out node and sequence of hooks */
164752419Sjulian	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
164871047Sjulian		TRAP_ERROR();
164952419Sjulian		return EINVAL;
165052419Sjulian	}
165152419Sjulian
165270700Sjulian	/*
165370700Sjulian	 * For an absolute address, jump to the starting node.
165470700Sjulian	 * Note that this holds a reference on the node for us.
165570700Sjulian	 * Don't forget to drop the reference if we don't need it.
165670700Sjulian	 */
165752419Sjulian	if (nodename) {
165870700Sjulian		node = ng_name2noderef(here, nodename);
165952419Sjulian		if (node == NULL) {
166071047Sjulian			TRAP_ERROR();
166152419Sjulian			return (ENOENT);
166252419Sjulian		}
166370700Sjulian	} else {
166470700Sjulian		if (here == NULL) {
166571047Sjulian			TRAP_ERROR();
166670700Sjulian			return (EINVAL);
166770700Sjulian		}
166852419Sjulian		node = here;
166970784Sjulian		NG_NODE_REF(node);
167070700Sjulian	}
167152419Sjulian
1672219827Sglebius	if (path == NULL) {
1673219827Sglebius		if (lasthook != NULL)
1674219827Sglebius			*lasthook = NULL;
1675219827Sglebius		*destp = node;
1676219827Sglebius		return (0);
1677219827Sglebius	}
1678219827Sglebius
167970700Sjulian	/*
1680152451Sglebius	 * Now follow the sequence of hooks
1681219827Sglebius	 *
1682219827Sglebius	 * XXXGL: The path may demolish as we go the sequence, but if
1683219827Sglebius	 * we hold the topology mutex at critical places, then, I hope,
1684219827Sglebius	 * we would always have valid pointers in hand, although the
1685219827Sglebius	 * path behind us may no longer exist.
168670700Sjulian	 */
1687219827Sglebius	for (;;) {
1688219827Sglebius		hook_p hook;
168952419Sjulian		char *segment;
169052419Sjulian
169152419Sjulian		/*
169252419Sjulian		 * Break out the next path segment. Replace the dot we just
1693219827Sglebius		 * found with a NUL; "path" points to the next segment (or the
169452419Sjulian		 * NUL at the end).
169552419Sjulian		 */
1696219827Sglebius		for (segment = path; *path != '\0'; path++) {
1697219827Sglebius			if (*path == '.') {
1698219827Sglebius				*path++ = '\0';
169952419Sjulian				break;
170052419Sjulian			}
170152419Sjulian		}
170252419Sjulian
170352419Sjulian		/* We have a segment, so look for a hook by that name */
170454096Sarchie		hook = ng_findhook(node, segment);
170552419Sjulian
1706219827Sglebius		mtx_lock(&ng_topo_mtx);
170752419Sjulian		/* Can't get there from here... */
1708229003Sglebius		if (hook == NULL || NG_HOOK_PEER(hook) == NULL ||
1709229003Sglebius		    NG_HOOK_NOT_VALID(hook) ||
1710229003Sglebius		    NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
171171047Sjulian			TRAP_ERROR();
171270784Sjulian			NG_NODE_UNREF(node);
1713219827Sglebius			mtx_unlock(&ng_topo_mtx);
171452419Sjulian			return (ENOENT);
171552419Sjulian		}
171652419Sjulian
171770700Sjulian		/*
1718152451Sglebius		 * Hop on over to the next node
171970700Sjulian		 * XXX
1720152451Sglebius		 * Big race conditions here as hooks and nodes go away
172170700Sjulian		 * *** Idea.. store an ng_ID_t in each hook and use that
172270700Sjulian		 * instead of the direct hook in this crawl?
172370700Sjulian		 */
172470700Sjulian		oldnode = node;
172570784Sjulian		if ((node = NG_PEER_NODE(hook)))
172670784Sjulian			NG_NODE_REF(node);	/* XXX RACE */
172770784Sjulian		NG_NODE_UNREF(oldnode);	/* XXX another race */
172870784Sjulian		if (NG_NODE_NOT_VALID(node)) {
172970784Sjulian			NG_NODE_UNREF(node);	/* XXX more races */
1730219827Sglebius			mtx_unlock(&ng_topo_mtx);
1731219827Sglebius			TRAP_ERROR();
1732219827Sglebius			return (ENXIO);
173370700Sjulian		}
173452419Sjulian
1735219827Sglebius		if (*path == '\0') {
1736219827Sglebius			if (lasthook != NULL) {
1737219827Sglebius				if (hook != NULL) {
1738219827Sglebius					*lasthook = NG_HOOK_PEER(hook);
1739219827Sglebius					NG_HOOK_REF(*lasthook);
1740219827Sglebius				} else
1741219827Sglebius					*lasthook = NULL;
1742219827Sglebius			}
1743219827Sglebius			mtx_unlock(&ng_topo_mtx);
1744219827Sglebius			*destp = node;
1745219827Sglebius			return (0);
1746219827Sglebius		}
1747219827Sglebius		mtx_unlock(&ng_topo_mtx);
174852419Sjulian	}
174952419Sjulian}
175052419Sjulian
175170700Sjulian/***************************************************************\
175270700Sjulian* Input queue handling.
175370700Sjulian* All activities are submitted to the node via the input queue
175470700Sjulian* which implements a multiple-reader/single-writer gate.
1755167677Srwatson* Items which cannot be handled immediately are queued.
175670700Sjulian*
175770700Sjulian* read-write queue locking inline functions			*
175870700Sjulian\***************************************************************/
175970700Sjulian
1760178228Smavstatic __inline void	ng_queue_rw(node_p node, item_p  item, int rw);
1761178228Smavstatic __inline item_p	ng_dequeue(node_p node, int *rw);
1762178228Smavstatic __inline item_p	ng_acquire_read(node_p node, item_p  item);
1763178228Smavstatic __inline item_p	ng_acquire_write(node_p node, item_p  item);
1764178228Smavstatic __inline void	ng_leave_read(node_p node);
1765178228Smavstatic __inline void	ng_leave_write(node_p node);
176670700Sjulian
176752419Sjulian/*
176870700Sjulian * Definition of the bits fields in the ng_queue flag word.
176970700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle
177070700Sjulian * with them.
177170700Sjulian *
177271902Sjulian * The ordering here may be important! don't shuffle these.
177352419Sjulian */
177470700Sjulian/*-
177570700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet)
177670700Sjulian                       |
177770700Sjulian                       V
177870700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
1779151973Sglebius  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
1780151973Sglebius  | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
1781151973Sglebius  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
178270700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
1783151973Sglebius  \___________________________ ____________________________/ | |
1784151973Sglebius                            V                                | |
1785151973Sglebius                  [active reader count]                      | |
178670700Sjulian                                                             | |
1787151973Sglebius            Operation Pending -------------------------------+ |
178870700Sjulian                                                               |
1789151973Sglebius          Active Writer ---------------------------------------+
179071902Sjulian
1791178039SmavNode queue has such semantics:
1792178039Smav- All flags modifications are atomic.
1793178039Smav- Reader count can be incremented only if there is no writer or pending flags.
1794178039Smav  As soon as this can't be done with single operation, it is implemented with
1795178039Smav  spin loop and atomic_cmpset().
1796178039Smav- Writer flag can be set only if there is no any bits set.
1797178039Smav  It is implemented with atomic_cmpset().
1798178039Smav- Pending flag can be set any time, but to avoid collision on queue processing
1799178039Smav  all queue fields are protected by the mutex.
1800178039Smav- Queue processing thread reads queue holding the mutex, but releases it while
1801178039Smav  processing. When queue is empty pending flag is removed.
1802178039Smav*/
180371902Sjulian
1804151973Sglebius#define WRITER_ACTIVE	0x00000001
1805151973Sglebius#define OP_PENDING	0x00000002
1806151973Sglebius#define READER_INCREMENT 0x00000004
1807151973Sglebius#define READER_MASK	0xfffffffc	/* Not valid if WRITER_ACTIVE is set */
1808151973Sglebius#define SAFETY_BARRIER	0x00100000	/* 128K items queued should be enough */
180971902Sjulian
181071902Sjulian/* Defines of more elaborate states on the queue */
1811151973Sglebius/* Mask of bits a new read cares about */
1812151973Sglebius#define NGQ_RMASK	(WRITER_ACTIVE|OP_PENDING)
181371902Sjulian
1814151973Sglebius/* Mask of bits a new write cares about */
181571902Sjulian#define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
181671902Sjulian
1817151973Sglebius/* Test to decide if there is something on the queue. */
1818151973Sglebius#define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
181971902Sjulian
1820151973Sglebius/* How to decide what the next queued item is. */
1821178228Smav#define HEAD_IS_READER(QP)  NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue))
1822178228Smav#define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */
1823151973Sglebius
1824151973Sglebius/* Read the status to decide if the next item on the queue can now run. */
1825151973Sglebius#define QUEUED_READER_CAN_PROCEED(QP)			\
1826151973Sglebius		(((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
1827151973Sglebius#define QUEUED_WRITER_CAN_PROCEED(QP)			\
1828151973Sglebius		(((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1829151973Sglebius
183071902Sjulian/* Is there a chance of getting ANY work off the queue? */
1831151973Sglebius#define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)				\
1832151973Sglebius	((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :		\
1833177953Smav				QUEUED_WRITER_CAN_PROCEED(QP))
183471902Sjulian
1835151238Sglebius#define NGQRW_R 0
1836151238Sglebius#define NGQRW_W 1
1837151238Sglebius
1838178228Smav#define NGQ2_WORKQ	0x00000001
1839178228Smav
184070700Sjulian/*
184170700Sjulian * Taking into account the current state of the queue and node, possibly take
184270700Sjulian * the next entry off the queue and return it. Return NULL if there was
184370700Sjulian * nothing we could return, either because there really was nothing there, or
184470700Sjulian * because the node was in a state where it cannot yet process the next item
184570700Sjulian * on the queue.
184670700Sjulian */
184770700Sjulianstatic __inline item_p
1848178228Smavng_dequeue(node_p node, int *rw)
184970700Sjulian{
185070700Sjulian	item_p item;
1851178228Smav	struct ng_queue *ngq = &node->nd_input_queue;
185271902Sjulian
1853178039Smav	/* This MUST be called with the mutex held. */
1854139039Sglebius	mtx_assert(&ngq->q_mtx, MA_OWNED);
1855178039Smav
1856178039Smav	/* If there is nothing queued, then just return. */
1857151973Sglebius	if (!QUEUE_ACTIVE(ngq)) {
1858154275Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
1859154275Sglebius		    "queue flags 0x%lx", __func__,
1860178228Smav		    node->nd_ID, node, ngq->q_flags);
1861151973Sglebius		return (NULL);
1862151973Sglebius	}
1863139039Sglebius
1864151973Sglebius	/*
1865151973Sglebius	 * From here, we can assume there is a head item.
1866151973Sglebius	 * We need to find out what it is and if it can be dequeued, given
1867151973Sglebius	 * the current state of the node.
1868151973Sglebius	 */
1869151973Sglebius	if (HEAD_IS_READER(ngq)) {
1870177953Smav		while (1) {
1871177953Smav			long t = ngq->q_flags;
1872177953Smav			if (t & WRITER_ACTIVE) {
1873178039Smav				/* There is writer, reader can't proceed. */
1874229003Sglebius				CTR4(KTR_NET, "%20s: node [%x] (%p) queued "
1875229003Sglebius				    "reader can't proceed; queue flags 0x%lx",
1876229003Sglebius				    __func__, node->nd_ID, node, t);
1877177953Smav				return (NULL);
1878177953Smav			}
1879178228Smav			if (atomic_cmpset_acq_int(&ngq->q_flags, t,
1880177953Smav			    t + READER_INCREMENT))
1881177953Smav				break;
1882177953Smav			cpu_spinwait();
1883151973Sglebius		}
1884178039Smav		/* We have got reader lock for the node. */
1885151238Sglebius		*rw = NGQRW_R;
1886178228Smav	} else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING,
1887177953Smav	    OP_PENDING + WRITER_ACTIVE)) {
1888178039Smav		/* We have got writer lock for the node. */
1889151238Sglebius		*rw = NGQRW_W;
189070700Sjulian	} else {
1891178039Smav		/* There is somebody other, writer can't proceed. */
1892229003Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer can't "
1893229003Sglebius		    "proceed; queue flags 0x%lx", __func__, node->nd_ID, node,
1894229003Sglebius		    ngq->q_flags);
1895151973Sglebius		return (NULL);
189670700Sjulian	}
189752419Sjulian
189870700Sjulian	/*
189970700Sjulian	 * Now we dequeue the request (whatever it may be) and correct the
190070700Sjulian	 * pending flags and the next and last pointers.
190170700Sjulian	 */
1902178228Smav	item = STAILQ_FIRST(&ngq->queue);
1903178228Smav	STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
1904178228Smav	if (STAILQ_EMPTY(&ngq->queue))
1905178228Smav		atomic_clear_int(&ngq->q_flags, OP_PENDING);
1906229003Sglebius	CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; queue "
1907229003Sglebius	    "flags 0x%lx", __func__, node->nd_ID, node, item, *rw ? "WRITER" :
1908229003Sglebius	    "READER", ngq->q_flags);
190970700Sjulian	return (item);
191070700Sjulian}
191152419Sjulian
191270700Sjulian/*
1913178039Smav * Queue a packet to be picked up later by someone else.
1914178039Smav * If the queue could be run now, add node to the queue handler's worklist.
191570700Sjulian */
191670700Sjulianstatic __inline void
1917178228Smavng_queue_rw(node_p node, item_p  item, int rw)
191870700Sjulian{
1919178228Smav	struct ng_queue *ngq = &node->nd_input_queue;
1920151973Sglebius	if (rw == NGQRW_W)
1921151973Sglebius		NGI_SET_WRITER(item);
1922151973Sglebius	else
1923151973Sglebius		NGI_SET_READER(item);
1924177953Smav
1925177953Smav	NG_QUEUE_LOCK(ngq);
1926177953Smav	/* Set OP_PENDING flag and enqueue the item. */
1927178228Smav	atomic_set_int(&ngq->q_flags, OP_PENDING);
1928178228Smav	STAILQ_INSERT_TAIL(&ngq->queue, item, el_next);
1929177953Smav
1930154275Sglebius	CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
1931178228Smav	    node->nd_ID, node, item, rw ? "WRITER" : "READER" );
1932229003Sglebius
193370700Sjulian	/*
1934151973Sglebius	 * We can take the worklist lock with the node locked
1935151973Sglebius	 * BUT NOT THE REVERSE!
1936151973Sglebius	 */
1937151973Sglebius	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
1938178228Smav		ng_worklist_add(node);
1939177953Smav	NG_QUEUE_UNLOCK(ngq);
194070700Sjulian}
194152419Sjulian
1942178039Smav/* Acquire reader lock on node. If node is busy, queue the packet. */
194370700Sjulianstatic __inline item_p
1944178228Smavng_acquire_read(node_p node, item_p item)
194552419Sjulian{
1946178228Smav	KASSERT(node != &ng_deadnode,
1947151974Sglebius	    ("%s: working on deadnode", __func__));
194852419Sjulian
1949177953Smav	/* Reader needs node without writer and pending items. */
1950229003Sglebius	for (;;) {
1951178228Smav		long t = node->nd_input_queue.q_flags;
1952177953Smav		if (t & NGQ_RMASK)
1953177953Smav			break; /* Node is not ready for reader. */
1954229003Sglebius		if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, t,
1955229003Sglebius		    t + READER_INCREMENT)) {
1956177953Smav	    		/* Successfully grabbed node */
1957177953Smav			CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
1958178228Smav			    __func__, node->nd_ID, node, item);
1959177953Smav			return (item);
1960177953Smav		}
1961177953Smav		cpu_spinwait();
1962177953Smav	};
196370700Sjulian
1964177953Smav	/* Queue the request for later. */
1965178228Smav	ng_queue_rw(node, item, NGQRW_R);
196670700Sjulian
1967148236Sglebius	return (NULL);
196870700Sjulian}
196970700Sjulian
1970178039Smav/* Acquire writer lock on node. If node is busy, queue the packet. */
197170700Sjulianstatic __inline item_p
1972178228Smavng_acquire_write(node_p node, item_p item)
197370700Sjulian{
1974178228Smav	KASSERT(node != &ng_deadnode,
1975151974Sglebius	    ("%s: working on deadnode", __func__));
1976151974Sglebius
1977177953Smav	/* Writer needs completely idle node. */
1978229003Sglebius	if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, 0,
1979229003Sglebius	    WRITER_ACTIVE)) {
1980177953Smav	    	/* Successfully grabbed node */
1981154275Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
1982178228Smav		    __func__, node->nd_ID, node, item);
198370700Sjulian		return (item);
198452419Sjulian	}
198552419Sjulian
1986177953Smav	/* Queue the request for later. */
1987178228Smav	ng_queue_rw(node, item, NGQRW_W);
198870700Sjulian
1989148236Sglebius	return (NULL);
199070700Sjulian}
199170700Sjulian
1992167385Sjulian#if 0
1993167385Sjulianstatic __inline item_p
1994178228Smavng_upgrade_write(node_p node, item_p item)
1995167385Sjulian{
1996178228Smav	struct ng_queue *ngq = &node->nd_input_queue;
1997178228Smav	KASSERT(node != &ng_deadnode,
1998167385Sjulian	    ("%s: working on deadnode", __func__));
1999167385Sjulian
2000167385Sjulian	NGI_SET_WRITER(item);
2001167385Sjulian
2002178228Smav	NG_QUEUE_LOCK(ngq);
2003167385Sjulian
2004167385Sjulian	/*
2005167385Sjulian	 * There will never be no readers as we are there ourselves.
2006167385Sjulian	 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2007167385Sjulian	 * The caller we are running from will call ng_leave_read()
2008167385Sjulian	 * soon, so we must account for that. We must leave again with the
2009167385Sjulian	 * READER lock. If we find other readers, then
2010167385Sjulian	 * queue the request for later. However "later" may be rignt now
2011167385Sjulian	 * if there are no readers. We don't really care if there are queued
2012167385Sjulian	 * items as we will bypass them anyhow.
2013167385Sjulian	 */
2014178228Smav	atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
2015178228Smav	if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) {
2016178228Smav		NG_QUEUE_UNLOCK(ngq);
2017167385Sjulian
2018167385Sjulian		/* It's just us, act on the item. */
2019167385Sjulian		/* will NOT drop writer lock when done */
2020167385Sjulian		ng_apply_item(node, item, 0);
2021167385Sjulian
2022167385Sjulian		/*
2023229003Sglebius		 * Having acted on the item, atomically
2024229003Sglebius		 * downgrade back to READER and finish up.
2025167385Sjulian	 	 */
2026229003Sglebius		atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2027167385Sjulian
2028167385Sjulian		/* Our caller will call ng_leave_read() */
2029167385Sjulian		return;
2030167385Sjulian	}
2031167385Sjulian	/*
2032167385Sjulian	 * It's not just us active, so queue us AT THE HEAD.
2033167385Sjulian	 * "Why?" I hear you ask.
2034167385Sjulian	 * Put us at the head of the queue as we've already been
2035167385Sjulian	 * through it once. If there is nothing else waiting,
2036167385Sjulian	 * set the correct flags.
2037167385Sjulian	 */
2038178228Smav	if (STAILQ_EMPTY(&ngq->queue)) {
2039167385Sjulian		/* We've gone from, 0 to 1 item in the queue */
2040178228Smav		atomic_set_int(&ngq->q_flags, OP_PENDING);
2041167385Sjulian
2042167385Sjulian		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
2043178228Smav		    node->nd_ID, node);
2044167385Sjulian	};
2045178228Smav	STAILQ_INSERT_HEAD(&ngq->queue, item, el_next);
2046178228Smav	CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
2047178228Smav	    __func__, node->nd_ID, node, item );
2048167385Sjulian
2049167385Sjulian	/* Reverse what we did above. That downgrades us back to reader */
2050178228Smav	atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2051177953Smav	if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2052178228Smav		ng_worklist_add(node);
2053178228Smav	NG_QUEUE_UNLOCK(ngq);
2054167385Sjulian
2055167385Sjulian	return;
2056167385Sjulian}
2057167385Sjulian#endif
2058167385Sjulian
2059178039Smav/* Release reader lock. */
206070700Sjulianstatic __inline void
2061178228Smavng_leave_read(node_p node)
206270700Sjulian{
2063178228Smav	atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT);
206470700Sjulian}
206570700Sjulian
2066178039Smav/* Release writer lock. */
206770700Sjulianstatic __inline void
2068178228Smavng_leave_write(node_p node)
206970700Sjulian{
2070178228Smav	atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE);
207170700Sjulian}
207270700Sjulian
2073178039Smav/* Purge node queue. Called on node shutdown. */
207470700Sjulianstatic void
2075178228Smavng_flush_input_queue(node_p node)
207670700Sjulian{
2077178228Smav	struct ng_queue *ngq = &node->nd_input_queue;
207870700Sjulian	item_p item;
2079151973Sglebius
2080168049Swkoszek	NG_QUEUE_LOCK(ngq);
2081178228Smav	while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) {
2082178228Smav		STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
2083178228Smav		if (STAILQ_EMPTY(&ngq->queue))
2084178228Smav			atomic_clear_int(&ngq->q_flags, OP_PENDING);
2085168049Swkoszek		NG_QUEUE_UNLOCK(ngq);
208670700Sjulian
2087151973Sglebius		/* If the item is supplying a callback, call it with an error */
2088177071Smav		if (item->apply != NULL) {
2089177071Smav			if (item->depth == 1)
2090177071Smav				item->apply->error = ENOENT;
2091177071Smav			if (refcount_release(&item->apply->refs)) {
2092177071Smav				(*item->apply->apply)(item->apply->context,
2093177071Smav				    item->apply->error);
2094177071Smav			}
2095151283Sglebius		}
2096132369Sjulian		NG_FREE_ITEM(item);
2097168049Swkoszek		NG_QUEUE_LOCK(ngq);
209870700Sjulian	}
2099168049Swkoszek	NG_QUEUE_UNLOCK(ngq);
210070700Sjulian}
210170700Sjulian
210270700Sjulian/***********************************************************************
210370700Sjulian* Externally visible method for sending or queueing messages or data.
210470700Sjulian***********************************************************************/
210570700Sjulian
210670700Sjulian/*
210771849Sjulian * The module code should have filled out the item correctly by this stage:
210870700Sjulian * Common:
210970700Sjulian *    reference to destination node.
211070700Sjulian *    Reference to destination rcv hook if relevant.
2111172806Smav *    apply pointer must be or NULL or reference valid struct ng_apply_info.
211270700Sjulian * Data:
211370700Sjulian *    pointer to mbuf
211470700Sjulian * Control_Message:
211570700Sjulian *    pointer to msg.
211670700Sjulian *    ID of original sender node. (return address)
211771849Sjulian * Function:
211871849Sjulian *    Function pointer
211971849Sjulian *    void * argument
212071849Sjulian *    integer argument
212170700Sjulian *
212270700Sjulian * The nodes have several routines and macros to help with this task:
212370700Sjulian */
212470700Sjulian
212570700Sjulianint
2126146281Sglebiusng_snd_item(item_p item, int flags)
212770700Sjulian{
2128172806Smav	hook_p hook;
2129172806Smav	node_p node;
2130146281Sglebius	int queue, rw;
2131172806Smav	struct ng_queue *ngq;
2132151256Sglebius	int error = 0;
213370700Sjulian
2134176046Smav	/* We are sending item, so it must be present! */
2135176046Smav	KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
2136172806Smav
213770784Sjulian#ifdef	NETGRAPH_DEBUG
2138152451Sglebius	_ngi_check(item, __FILE__, __LINE__);
213970700Sjulian#endif
214070700Sjulian
2141176046Smav	/* Item was sent once more, postpone apply() call. */
2142172806Smav	if (item->apply)
2143172806Smav		refcount_acquire(&item->apply->refs);
2144146281Sglebius
2145172806Smav	node = NGI_NODE(item);
2146176046Smav	/* Node is never optional. */
2147176046Smav	KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
2148172806Smav
2149175850Smav	hook = NGI_HOOK(item);
2150176046Smav	/* Valid hook and mbuf are mandatory for data. */
2151176046Smav	if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
2152176046Smav		KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
2153131123Sjulian		if (NGI_M(item) == NULL)
2154172806Smav			ERROUT(EINVAL);
215570700Sjulian		CHECK_DATA_MBUF(NGI_M(item));
215669922Sjulian	}
2157149505Sglebius
215870700Sjulian	/*
2159176046Smav	 * If the item or the node specifies single threading, force
2160176046Smav	 * writer semantics. Similarly, the node may say one hook always
2161176046Smav	 * produces writers. These are overrides.
216270700Sjulian	 */
2163176567Smav	if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
2164176046Smav	    (node->nd_flags & NGF_FORCE_WRITER) ||
2165176046Smav	    (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
2166176046Smav		rw = NGQRW_W;
2167176046Smav	} else {
2168176046Smav		rw = NGQRW_R;
2169176046Smav	}
2170149505Sglebius
2171175847Smav	/*
2172194012Szec	 * If sender or receiver requests queued delivery, or call graph
2173194012Szec	 * loops back from outbound to inbound path, or stack usage
2174175847Smav	 * level is dangerous - enqueue message.
2175175847Smav	 */
2176175847Smav	if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
2177175847Smav		queue = 1;
2178194012Szec	} else if (hook && (hook->hk_flags & HK_TO_INBOUND) &&
2179194012Szec	    curthread->td_ng_outbound) {
2180194012Szec		queue = 1;
2181176046Smav	} else {
2182176046Smav		queue = 0;
2183175847Smav#ifdef GET_STACK_USAGE
2184175868Smav		/*
2185175871Smarck		 * Most of netgraph nodes have small stack consumption and
2186176046Smav		 * for them 25% of free stack space is more than enough.
2187175868Smav		 * Nodes/hooks with higher stack usage should be marked as
2188175889Smarck		 * HI_STACK. For them 50% of stack will be guaranteed then.
2189176046Smav		 * XXX: Values 25% and 50% are completely empirical.
2190175868Smav		 */
2191176046Smav		size_t	st, su, sl;
2192175847Smav		GET_STACK_USAGE(st, su);
2193176046Smav		sl = st - su;
2194229003Sglebius		if ((sl * 4 < st) || ((sl * 2 < st) &&
2195229003Sglebius		    ((node->nd_flags & NGF_HI_STACK) || (hook &&
2196229003Sglebius		    (hook->hk_flags & HK_HI_STACK)))))
2197175847Smav			queue = 1;
2198176046Smav#endif
2199175847Smav	}
2200175847Smav
220170700Sjulian	if (queue) {
2202177953Smav		item->depth = 1;
220370700Sjulian		/* Put it on the queue for that node*/
2204178228Smav		ng_queue_rw(node, item, rw);
2205176046Smav		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
220670700Sjulian	}
220769922Sjulian
220870700Sjulian	/*
220970700Sjulian	 * We already decided how we will be queueud or treated.
221070700Sjulian	 * Try get the appropriate operating permission.
221170700Sjulian	 */
2212151256Sglebius 	if (rw == NGQRW_R)
2213178228Smav		item = ng_acquire_read(node, item);
2214151256Sglebius	else
2215178228Smav		item = ng_acquire_write(node, item);
221652419Sjulian
2217176046Smav	/* Item was queued while trying to get permission. */
2218176046Smav	if (item == NULL)
2219176046Smav		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
222052419Sjulian
222174078Sjulian	NGI_GET_NODE(item, node); /* zaps stored node */
222252419Sjulian
2223177071Smav	item->depth++;
2224170180Sglebius	error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
222574078Sjulian
2226177953Smav	/* If something is waiting on queue and ready, schedule it. */
2227178228Smav	ngq = &node->nd_input_queue;
2228177953Smav	if (QUEUE_ACTIVE(ngq)) {
2229177953Smav		NG_QUEUE_LOCK(ngq);
2230177953Smav		if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2231178228Smav			ng_worklist_add(node);
2232177953Smav		NG_QUEUE_UNLOCK(ngq);
2233177953Smav	}
2234177953Smav
223552419Sjulian	/*
2236177953Smav	 * Node may go away as soon as we remove the reference.
2237177953Smav	 * Whatever we do, DO NOT access the node again!
223874078Sjulian	 */
2239177953Smav	NG_NODE_UNREF(node);
224074078Sjulian
224171849Sjulian	return (error);
2242172806Smav
2243172806Smavdone:
2244176046Smav	/* If was not sent, apply callback here. */
2245177071Smav	if (item->apply != NULL) {
2246177071Smav		if (item->depth == 0 && error != 0)
2247177071Smav			item->apply->error = error;
2248177071Smav		if (refcount_release(&item->apply->refs)) {
2249177071Smav			(*item->apply->apply)(item->apply->context,
2250177071Smav			    item->apply->error);
2251177071Smav		}
2252177071Smav	}
2253175850Smav
2254172806Smav	NG_FREE_ITEM(item);
2255172806Smav	return (error);
225652419Sjulian}
225752419Sjulian
225852419Sjulian/*
225970700Sjulian * We have an item that was possibly queued somewhere.
226070700Sjulian * It should contain all the information needed
226170700Sjulian * to run it on the appropriate node/hook.
2262172806Smav * If there is apply pointer and we own the last reference, call apply().
226352419Sjulian */
2264170180Sglebiusstatic int
2265151238Sglebiusng_apply_item(node_p node, item_p item, int rw)
226652419Sjulian{
226770700Sjulian	hook_p  hook;
226870700Sjulian	ng_rcvdata_t *rcvdata;
226971885Sjulian	ng_rcvmsg_t *rcvmsg;
2270172806Smav	struct ng_apply_info *apply;
2271177071Smav	int	error = 0, depth;
227252419Sjulian
2273176046Smav	/* Node and item are never optional. */
2274176046Smav	KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
2275176046Smav	KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
2276176046Smav
227771849Sjulian	NGI_GET_HOOK(item, hook); /* clears stored hook */
227870784Sjulian#ifdef	NETGRAPH_DEBUG
2279152451Sglebius	_ngi_check(item, __FILE__, __LINE__);
228070700Sjulian#endif
2281147774Sglebius
2282172806Smav	apply = item->apply;
2283177071Smav	depth = item->depth;
2284147774Sglebius
228571047Sjulian	switch (item->el_flags & NGQF_TYPE) {
228670700Sjulian	case NGQF_DATA:
228770700Sjulian		/*
228870700Sjulian		 * Check things are still ok as when we were queued.
228970700Sjulian		 */
2290176046Smav		KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
2291176046Smav		if (NG_HOOK_NOT_VALID(hook) ||
2292176046Smav		    NG_NODE_NOT_VALID(node)) {
2293167402Sjulian			error = EIO;
229470700Sjulian			NG_FREE_ITEM(item);
229571885Sjulian			break;
229670700Sjulian		}
229771885Sjulian		/*
229871885Sjulian		 * If no receive method, just silently drop it.
2299229003Sglebius		 * Give preference to the hook over-ride method.
230071885Sjulian		 */
2301229003Sglebius		if ((!(rcvdata = hook->hk_rcvdata)) &&
2302229003Sglebius		    (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
230371885Sjulian			error = 0;
230471885Sjulian			NG_FREE_ITEM(item);
230571885Sjulian			break;
230671885Sjulian		}
2307167402Sjulian		error = (*rcvdata)(hook, item);
230870700Sjulian		break;
230970700Sjulian	case NGQF_MESG:
2310175850Smav		if (hook && NG_HOOK_NOT_VALID(hook)) {
2311175850Smav			/*
2312175850Smav			 * The hook has been zapped then we can't use it.
2313175850Smav			 * Immediately drop its reference.
2314175850Smav			 * The message may not need it.
2315175850Smav			 */
2316175850Smav			NG_HOOK_UNREF(hook);
2317175850Smav			hook = NULL;
231870700Sjulian		}
231970700Sjulian		/*
232070700Sjulian		 * Similarly, if the node is a zombie there is
232170700Sjulian		 * nothing we can do with it, drop everything.
232270700Sjulian		 */
232370784Sjulian		if (NG_NODE_NOT_VALID(node)) {
232471047Sjulian			TRAP_ERROR();
2325167402Sjulian			error = EINVAL;
232670700Sjulian			NG_FREE_ITEM(item);
2327175850Smav			break;
232870700Sjulian		}
2329175850Smav		/*
2330175850Smav		 * Call the appropriate message handler for the object.
2331175850Smav		 * It is up to the message handler to free the message.
2332175850Smav		 * If it's a generic message, handle it generically,
2333175850Smav		 * otherwise call the type's message handler (if it exists).
2334175850Smav		 * XXX (race). Remember that a queued message may
2335175850Smav		 * reference a node or hook that has just been
2336175850Smav		 * invalidated. It will exist as the queue code
2337175850Smav		 * is holding a reference, but..
2338175850Smav		 */
2339175850Smav		if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
2340175850Smav		    ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
2341175850Smav			error = ng_generic_msg(node, item, hook);
2342175850Smav			break;
2343175850Smav		}
2344175850Smav		if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
2345175850Smav		    (!(rcvmsg = node->nd_type->rcvmsg))) {
2346175850Smav			TRAP_ERROR();
2347175850Smav			error = 0;
2348175850Smav			NG_FREE_ITEM(item);
2349175850Smav			break;
2350175850Smav		}
2351175850Smav		error = (*rcvmsg)(node, item, hook);
235270700Sjulian		break;
235371047Sjulian	case NGQF_FN:
2354173605Sglebius	case NGQF_FN2:
235571047Sjulian		/*
2356182995Smav		 * In the case of the shutdown message we allow it to hit
235771849Sjulian		 * even if the node is invalid.
235871047Sjulian		 */
2359182995Smav		if (NG_NODE_NOT_VALID(node) &&
2360182995Smav		    NGI_FN(item) != &ng_rmnode) {
236171047Sjulian			TRAP_ERROR();
2362167402Sjulian			error = EINVAL;
2363143384Sglebius			NG_FREE_ITEM(item);
236471047Sjulian			break;
236571047Sjulian		}
2366182995Smav		/* Same is about some internal functions and invalid hook. */
2367182995Smav		if (hook && NG_HOOK_NOT_VALID(hook) &&
2368182995Smav		    NGI_FN2(item) != &ng_con_part2 &&
2369182995Smav		    NGI_FN2(item) != &ng_con_part3 &&
2370182995Smav		    NGI_FN(item) != &ng_rmhook_part2) {
2371182995Smav			TRAP_ERROR();
2372182995Smav			error = EINVAL;
2373182995Smav			NG_FREE_ITEM(item);
2374182995Smav			break;
2375182995Smav		}
2376182995Smav
2377173605Sglebius		if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
2378173605Sglebius			(*NGI_FN(item))(node, hook, NGI_ARG1(item),
2379173605Sglebius			    NGI_ARG2(item));
2380172806Smav			NG_FREE_ITEM(item);
2381173605Sglebius		} else	/* it is NGQF_FN2 */
2382173605Sglebius			error = (*NGI_FN2(item))(node, item, hook);
2383172806Smav		break;
238470700Sjulian	}
238570700Sjulian	/*
238670700Sjulian	 * We held references on some of the resources
238770700Sjulian	 * that we took from the item. Now that we have
238870700Sjulian	 * finished doing everything, drop those references.
238970700Sjulian	 */
2390175850Smav	if (hook)
239170784Sjulian		NG_HOOK_UNREF(hook);
239270700Sjulian
2393176046Smav 	if (rw == NGQRW_R)
2394178228Smav		ng_leave_read(node);
2395176046Smav	else
2396178228Smav		ng_leave_write(node);
2397147774Sglebius
2398147774Sglebius	/* Apply callback. */
2399177071Smav	if (apply != NULL) {
2400177071Smav		if (depth == 1 && error != 0)
2401177071Smav			apply->error = error;
2402177071Smav		if (refcount_release(&apply->refs))
2403177071Smav			(*apply->apply)(apply->context, apply->error);
2404177071Smav	}
2405147774Sglebius
2406170180Sglebius	return (error);
240770700Sjulian}
240870700Sjulian
240970700Sjulian/***********************************************************************
241070700Sjulian * Implement the 'generic' control messages
241170700Sjulian ***********************************************************************/
241270700Sjulianstatic int
241370700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook)
241470700Sjulian{
241570700Sjulian	int error = 0;
241670700Sjulian	struct ng_mesg *msg;
241770700Sjulian	struct ng_mesg *resp = NULL;
241870700Sjulian
241970700Sjulian	NGI_GET_MSG(item, msg);
242052419Sjulian	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
242171047Sjulian		TRAP_ERROR();
242270700Sjulian		error = EINVAL;
242370700Sjulian		goto out;
242452419Sjulian	}
242552419Sjulian	switch (msg->header.cmd) {
242652419Sjulian	case NGM_SHUTDOWN:
242771849Sjulian		ng_rmnode(here, NULL, NULL, 0);
242852419Sjulian		break;
242952419Sjulian	case NGM_MKPEER:
243052419Sjulian	    {
243152419Sjulian		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
243252419Sjulian
243352419Sjulian		if (msg->header.arglen != sizeof(*mkp)) {
243471047Sjulian			TRAP_ERROR();
243570700Sjulian			error = EINVAL;
243670700Sjulian			break;
243752419Sjulian		}
243852419Sjulian		mkp->type[sizeof(mkp->type) - 1] = '\0';
243952419Sjulian		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
244052419Sjulian		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
244152419Sjulian		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
244252419Sjulian		break;
244352419Sjulian	    }
244452419Sjulian	case NGM_CONNECT:
244552419Sjulian	    {
244652419Sjulian		struct ngm_connect *const con =
244752419Sjulian			(struct ngm_connect *) msg->data;
244852419Sjulian		node_p node2;
244952419Sjulian
245052419Sjulian		if (msg->header.arglen != sizeof(*con)) {
245171047Sjulian			TRAP_ERROR();
245270700Sjulian			error = EINVAL;
245370700Sjulian			break;
245452419Sjulian		}
245552419Sjulian		con->path[sizeof(con->path) - 1] = '\0';
245652419Sjulian		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
245752419Sjulian		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
245870700Sjulian		/* Don't forget we get a reference.. */
245970700Sjulian		error = ng_path2noderef(here, con->path, &node2, NULL);
246052419Sjulian		if (error)
246152419Sjulian			break;
2462172806Smav		error = ng_con_nodes(item, here, con->ourhook,
2463172806Smav		    node2, con->peerhook);
246470784Sjulian		NG_NODE_UNREF(node2);
246552419Sjulian		break;
246652419Sjulian	    }
246752419Sjulian	case NGM_NAME:
246852419Sjulian	    {
246952419Sjulian		struct ngm_name *const nam = (struct ngm_name *) msg->data;
247052419Sjulian
247152419Sjulian		if (msg->header.arglen != sizeof(*nam)) {
247271047Sjulian			TRAP_ERROR();
247370700Sjulian			error = EINVAL;
247470700Sjulian			break;
247552419Sjulian		}
247652419Sjulian		nam->name[sizeof(nam->name) - 1] = '\0';
247752419Sjulian		error = ng_name_node(here, nam->name);
247852419Sjulian		break;
247952419Sjulian	    }
248052419Sjulian	case NGM_RMHOOK:
248152419Sjulian	    {
248252419Sjulian		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
248352419Sjulian		hook_p hook;
248452419Sjulian
248552419Sjulian		if (msg->header.arglen != sizeof(*rmh)) {
248671047Sjulian			TRAP_ERROR();
248770700Sjulian			error = EINVAL;
248870700Sjulian			break;
248952419Sjulian		}
249052419Sjulian		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
249154096Sarchie		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
249252419Sjulian			ng_destroy_hook(hook);
249352419Sjulian		break;
249452419Sjulian	    }
249552419Sjulian	case NGM_NODEINFO:
249652419Sjulian	    {
249752419Sjulian		struct nodeinfo *ni;
249852419Sjulian
249970700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
250052419Sjulian		if (resp == NULL) {
250152419Sjulian			error = ENOMEM;
250252419Sjulian			break;
250352419Sjulian		}
250452419Sjulian
250552419Sjulian		/* Fill in node info */
250670700Sjulian		ni = (struct nodeinfo *) resp->data;
250770784Sjulian		if (NG_NODE_HAS_NAME(here))
2508125028Sharti			strcpy(ni->name, NG_NODE_NAME(here));
2509125028Sharti		strcpy(ni->type, here->nd_type->name);
251052722Sjulian		ni->id = ng_node2ID(here);
251170784Sjulian		ni->hooks = here->nd_numhooks;
251252419Sjulian		break;
251352419Sjulian	    }
251452419Sjulian	case NGM_LISTHOOKS:
251552419Sjulian	    {
251670784Sjulian		const int nhooks = here->nd_numhooks;
251752419Sjulian		struct hooklist *hl;
251852419Sjulian		struct nodeinfo *ni;
251952419Sjulian		hook_p hook;
252052419Sjulian
252152419Sjulian		/* Get response struct */
2522229003Sglebius		NG_MKRESPONSE(resp, msg, sizeof(*hl) +
2523229003Sglebius		    (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
252452419Sjulian		if (resp == NULL) {
252552419Sjulian			error = ENOMEM;
252652419Sjulian			break;
252752419Sjulian		}
252870700Sjulian		hl = (struct hooklist *) resp->data;
252952419Sjulian		ni = &hl->nodeinfo;
253052419Sjulian
253152419Sjulian		/* Fill in node info */
253270784Sjulian		if (NG_NODE_HAS_NAME(here))
2533125028Sharti			strcpy(ni->name, NG_NODE_NAME(here));
2534125028Sharti		strcpy(ni->type, here->nd_type->name);
253552722Sjulian		ni->id = ng_node2ID(here);
253652419Sjulian
253752419Sjulian		/* Cycle through the linked list of hooks */
253852419Sjulian		ni->hooks = 0;
253970784Sjulian		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
254052419Sjulian			struct linkinfo *const link = &hl->link[ni->hooks];
254152419Sjulian
254252419Sjulian			if (ni->hooks >= nhooks) {
254352419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
254487599Sobrien				    __func__, "hooks");
254552419Sjulian				break;
254652419Sjulian			}
254770784Sjulian			if (NG_HOOK_NOT_VALID(hook))
254852419Sjulian				continue;
2549125028Sharti			strcpy(link->ourhook, NG_HOOK_NAME(hook));
2550125028Sharti			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
255170784Sjulian			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
2552125028Sharti				strcpy(link->nodeinfo.name,
2553125028Sharti				    NG_PEER_NODE_NAME(hook));
2554125028Sharti			strcpy(link->nodeinfo.type,
2555125028Sharti			   NG_PEER_NODE(hook)->nd_type->name);
255670784Sjulian			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
255770784Sjulian			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
255852419Sjulian			ni->hooks++;
255952419Sjulian		}
256052419Sjulian		break;
256152419Sjulian	    }
256252419Sjulian
256352419Sjulian	case NGM_LISTNAMES:
256452419Sjulian	case NGM_LISTNODES:
256552419Sjulian	    {
256652419Sjulian		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
256752419Sjulian		struct namelist *nl;
256852419Sjulian		node_p node;
2569176802Smav		int num = 0, i;
257052419Sjulian
2571176802Smav		mtx_lock(&ng_namehash_mtx);
257252419Sjulian		/* Count number of nodes */
2573176802Smav		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2574181803Sbz			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2575176802Smav				if (NG_NODE_IS_VALID(node) &&
2576176802Smav				    (unnamed || NG_NODE_HAS_NAME(node))) {
2577176802Smav					num++;
2578176802Smav				}
257970912Sjulian			}
258052419Sjulian		}
2581176802Smav		mtx_unlock(&ng_namehash_mtx);
258252419Sjulian
258352419Sjulian		/* Get response struct */
2584229003Sglebius		NG_MKRESPONSE(resp, msg, sizeof(*nl) +
2585229003Sglebius		    (num * sizeof(struct nodeinfo)), M_NOWAIT);
258652419Sjulian		if (resp == NULL) {
258752419Sjulian			error = ENOMEM;
258852419Sjulian			break;
258952419Sjulian		}
259070700Sjulian		nl = (struct namelist *) resp->data;
259152419Sjulian
259252419Sjulian		/* Cycle through the linked list of nodes */
259352419Sjulian		nl->numnames = 0;
2594176802Smav		mtx_lock(&ng_namehash_mtx);
2595176802Smav		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
2596181803Sbz			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2597176802Smav				struct nodeinfo *const np =
2598176802Smav				    &nl->nodeinfo[nl->numnames];
259952419Sjulian
2600176802Smav				if (NG_NODE_NOT_VALID(node))
2601176802Smav					continue;
2602176802Smav				if (!unnamed && (! NG_NODE_HAS_NAME(node)))
2603176802Smav					continue;
2604176802Smav				if (nl->numnames >= num) {
2605176802Smav					log(LOG_ERR, "%s: number of nodes changed\n",
2606176802Smav					    __func__);
2607176802Smav					break;
2608176802Smav				}
2609176802Smav				if (NG_NODE_HAS_NAME(node))
2610176802Smav					strcpy(np->name, NG_NODE_NAME(node));
2611176802Smav				strcpy(np->type, node->nd_type->name);
2612176802Smav				np->id = ng_node2ID(node);
2613176802Smav				np->hooks = node->nd_numhooks;
2614176802Smav				nl->numnames++;
261552419Sjulian			}
261652419Sjulian		}
2617176802Smav		mtx_unlock(&ng_namehash_mtx);
261852419Sjulian		break;
261952419Sjulian	    }
262052419Sjulian
262152419Sjulian	case NGM_LISTTYPES:
262252419Sjulian	    {
262352419Sjulian		struct typelist *tl;
262452419Sjulian		struct ng_type *type;
262552419Sjulian		int num = 0;
262652419Sjulian
262772200Sbmilekic		mtx_lock(&ng_typelist_mtx);
262852419Sjulian		/* Count number of types */
262970912Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
263052419Sjulian			num++;
263170912Sjulian		}
263272200Sbmilekic		mtx_unlock(&ng_typelist_mtx);
263352419Sjulian
263452419Sjulian		/* Get response struct */
2635229003Sglebius		NG_MKRESPONSE(resp, msg, sizeof(*tl) +
2636229003Sglebius		    (num * sizeof(struct typeinfo)), M_NOWAIT);
263752419Sjulian		if (resp == NULL) {
263852419Sjulian			error = ENOMEM;
263952419Sjulian			break;
264052419Sjulian		}
264170700Sjulian		tl = (struct typelist *) resp->data;
264252419Sjulian
264352419Sjulian		/* Cycle through the linked list of types */
264452419Sjulian		tl->numtypes = 0;
264572200Sbmilekic		mtx_lock(&ng_typelist_mtx);
264670700Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
264752419Sjulian			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
264852419Sjulian
264952419Sjulian			if (tl->numtypes >= num) {
265052419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
265187599Sobrien				    __func__, "types");
265252419Sjulian				break;
265352419Sjulian			}
2654125028Sharti			strcpy(tp->type_name, type->name);
265571603Sjulian			tp->numnodes = type->refs - 1; /* don't count list */
265652419Sjulian			tl->numtypes++;
265752419Sjulian		}
265872200Sbmilekic		mtx_unlock(&ng_typelist_mtx);
265952419Sjulian		break;
266052419Sjulian	    }
266152419Sjulian
266253913Sarchie	case NGM_BINARY2ASCII:
266353913Sarchie	    {
266464510Sarchie		int bufSize = 20 * 1024;	/* XXX hard coded constant */
266553913Sarchie		const struct ng_parse_type *argstype;
266653913Sarchie		const struct ng_cmdlist *c;
266770700Sjulian		struct ng_mesg *binary, *ascii;
266853913Sarchie
266953913Sarchie		/* Data area must contain a valid netgraph message */
267053913Sarchie		binary = (struct ng_mesg *)msg->data;
2671152451Sglebius		if (msg->header.arglen < sizeof(struct ng_mesg) ||
2672152451Sglebius		    (msg->header.arglen - sizeof(struct ng_mesg) <
2673152451Sglebius		    binary->header.arglen)) {
267471047Sjulian			TRAP_ERROR();
267553913Sarchie			error = EINVAL;
267653913Sarchie			break;
267753913Sarchie		}
267853913Sarchie
267970700Sjulian		/* Get a response message with lots of room */
268070700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
268170159Sjulian		if (resp == NULL) {
268253913Sarchie			error = ENOMEM;
268353913Sarchie			break;
268453913Sarchie		}
268570700Sjulian		ascii = (struct ng_mesg *)resp->data;
268653913Sarchie
268753913Sarchie		/* Copy binary message header to response message payload */
268853913Sarchie		bcopy(binary, ascii, sizeof(*binary));
268953913Sarchie
269053913Sarchie		/* Find command by matching typecookie and command number */
2691229003Sglebius		for (c = here->nd_type->cmdlist; c != NULL && c->name != NULL;
2692229003Sglebius		    c++) {
2693229003Sglebius			if (binary->header.typecookie == c->cookie &&
2694229003Sglebius			    binary->header.cmd == c->cmd)
269553913Sarchie				break;
269653913Sarchie		}
269753913Sarchie		if (c == NULL || c->name == NULL) {
269853913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
2699229003Sglebius				if (binary->header.typecookie == c->cookie &&
2700229003Sglebius				    binary->header.cmd == c->cmd)
270153913Sarchie					break;
270253913Sarchie			}
270353913Sarchie			if (c->name == NULL) {
270470700Sjulian				NG_FREE_MSG(resp);
270553913Sarchie				error = ENOSYS;
270653913Sarchie				break;
270753913Sarchie			}
270853913Sarchie		}
270953913Sarchie
271053913Sarchie		/* Convert command name to ASCII */
271153913Sarchie		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
271253913Sarchie		    "%s", c->name);
271353913Sarchie
271453913Sarchie		/* Convert command arguments to ASCII */
271553913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
271653913Sarchie		    c->respType : c->mesgType;
271770912Sjulian		if (argstype == NULL) {
271853913Sarchie			*ascii->data = '\0';
271970912Sjulian		} else {
272053913Sarchie			if ((error = ng_unparse(argstype,
272153913Sarchie			    (u_char *)binary->data,
272253913Sarchie			    ascii->data, bufSize)) != 0) {
272370700Sjulian				NG_FREE_MSG(resp);
272453913Sarchie				break;
272553913Sarchie			}
272653913Sarchie		}
272753913Sarchie
272853913Sarchie		/* Return the result as struct ng_mesg plus ASCII string */
272953913Sarchie		bufSize = strlen(ascii->data) + 1;
273053913Sarchie		ascii->header.arglen = bufSize;
273170700Sjulian		resp->header.arglen = sizeof(*ascii) + bufSize;
273253913Sarchie		break;
273353913Sarchie	    }
273453913Sarchie
273553913Sarchie	case NGM_ASCII2BINARY:
273653913Sarchie	    {
2737208036Szec		int bufSize = 20 * 1024;	/* XXX hard coded constant */
273853913Sarchie		const struct ng_cmdlist *c;
273953913Sarchie		const struct ng_parse_type *argstype;
274070700Sjulian		struct ng_mesg *ascii, *binary;
274159178Sarchie		int off = 0;
274253913Sarchie
274353913Sarchie		/* Data area must contain at least a struct ng_mesg + '\0' */
274453913Sarchie		ascii = (struct ng_mesg *)msg->data;
2745152451Sglebius		if ((msg->header.arglen < sizeof(*ascii) + 1) ||
2746152451Sglebius		    (ascii->header.arglen < 1) ||
2747152451Sglebius		    (msg->header.arglen < sizeof(*ascii) +
2748152451Sglebius		    ascii->header.arglen)) {
274971047Sjulian			TRAP_ERROR();
275053913Sarchie			error = EINVAL;
275153913Sarchie			break;
275253913Sarchie		}
275353913Sarchie		ascii->data[ascii->header.arglen - 1] = '\0';
275453913Sarchie
275570700Sjulian		/* Get a response message with lots of room */
275670700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
275770159Sjulian		if (resp == NULL) {
275853913Sarchie			error = ENOMEM;
275953913Sarchie			break;
276053913Sarchie		}
276170700Sjulian		binary = (struct ng_mesg *)resp->data;
276253913Sarchie
276353913Sarchie		/* Copy ASCII message header to response message payload */
276453913Sarchie		bcopy(ascii, binary, sizeof(*ascii));
276553913Sarchie
276653913Sarchie		/* Find command by matching ASCII command string */
276770784Sjulian		for (c = here->nd_type->cmdlist;
276853913Sarchie		    c != NULL && c->name != NULL; c++) {
276953913Sarchie			if (strcmp(ascii->header.cmdstr, c->name) == 0)
277053913Sarchie				break;
277153913Sarchie		}
277253913Sarchie		if (c == NULL || c->name == NULL) {
277353913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
277453913Sarchie				if (strcmp(ascii->header.cmdstr, c->name) == 0)
277553913Sarchie					break;
277653913Sarchie			}
277753913Sarchie			if (c->name == NULL) {
277870700Sjulian				NG_FREE_MSG(resp);
277953913Sarchie				error = ENOSYS;
278053913Sarchie				break;
278153913Sarchie			}
278253913Sarchie		}
278353913Sarchie
278453913Sarchie		/* Convert command name to binary */
278553913Sarchie		binary->header.cmd = c->cmd;
278653913Sarchie		binary->header.typecookie = c->cookie;
278753913Sarchie
278853913Sarchie		/* Convert command arguments to binary */
278953913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
279053913Sarchie		    c->respType : c->mesgType;
279170912Sjulian		if (argstype == NULL) {
279253913Sarchie			bufSize = 0;
279370912Sjulian		} else {
2794229003Sglebius			if ((error = ng_parse(argstype, ascii->data, &off,
2795229003Sglebius			    (u_char *)binary->data, &bufSize)) != 0) {
279670700Sjulian				NG_FREE_MSG(resp);
279753913Sarchie				break;
279853913Sarchie			}
279953913Sarchie		}
280053913Sarchie
280153913Sarchie		/* Return the result */
280253913Sarchie		binary->header.arglen = bufSize;
280370700Sjulian		resp->header.arglen = sizeof(*binary) + bufSize;
280453913Sarchie		break;
280553913Sarchie	    }
280653913Sarchie
280762471Sphk	case NGM_TEXT_CONFIG:
280852419Sjulian	case NGM_TEXT_STATUS:
280952419Sjulian		/*
281052419Sjulian		 * This one is tricky as it passes the command down to the
281152419Sjulian		 * actual node, even though it is a generic type command.
281270700Sjulian		 * This means we must assume that the item/msg is already freed
281352419Sjulian		 * when control passes back to us.
281452419Sjulian		 */
281570784Sjulian		if (here->nd_type->rcvmsg != NULL) {
281670700Sjulian			NGI_MSG(item) = msg; /* put it back as we found it */
281770784Sjulian			return((*here->nd_type->rcvmsg)(here, item, lasthook));
281852419Sjulian		}
281952419Sjulian		/* Fall through if rcvmsg not supported */
282052419Sjulian	default:
282171047Sjulian		TRAP_ERROR();
282252419Sjulian		error = EINVAL;
282352419Sjulian	}
282470700Sjulian	/*
282570700Sjulian	 * Sometimes a generic message may be statically allocated
2826229003Sglebius	 * to avoid problems with allocating when in tight memory situations.
282770700Sjulian	 * Don't free it if it is so.
282870700Sjulian	 * I break them appart here, because erros may cause a free if the item
282970700Sjulian	 * in which case we'd be doing it twice.
283070700Sjulian	 * they are kept together above, to simplify freeing.
283170700Sjulian	 */
283270700Sjulianout:
283370700Sjulian	NG_RESPOND_MSG(error, here, item, resp);
2834185179Smav	NG_FREE_MSG(msg);
283552419Sjulian	return (error);
283652419Sjulian}
283752419Sjulian
283852419Sjulian/************************************************************************
2839146213Sglebius			Queue element get/free routines
2840146213Sglebius************************************************************************/
2841146213Sglebius
2842146213Sglebiusuma_zone_t			ng_qzone;
2843178259Smavuma_zone_t			ng_qdzone;
2844186093Smavstatic int			numthreads = 0; /* number of queue threads */
2845176849Smavstatic int			maxalloc = 4096;/* limit the damage of a leak */
2846176849Smavstatic int			maxdata = 512;	/* limit the damage of a DoS */
2847146213Sglebius
2848186093SmavTUNABLE_INT("net.graph.threads", &numthreads);
2849186093SmavSYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads,
2850186093Smav    0, "Number of queue processing threads");
2851146213SglebiusTUNABLE_INT("net.graph.maxalloc", &maxalloc);
2852146213SglebiusSYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
2853178259Smav    0, "Maximum number of non-data queue items to allocate");
2854176849SmavTUNABLE_INT("net.graph.maxdata", &maxdata);
2855178259SmavSYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata,
2856178259Smav    0, "Maximum number of data queue items to allocate");
2857146213Sglebius
2858146213Sglebius#ifdef	NETGRAPH_DEBUG
2859146213Sglebiusstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
2860229003Sglebiusstatic int allocated;	/* number of items malloc'd */
2861146213Sglebius#endif
2862146213Sglebius
2863146213Sglebius/*
2864146213Sglebius * Get a queue entry.
2865146213Sglebius * This is usually called when a packet first enters netgraph.
2866146213Sglebius * By definition, this is usually from an interrupt, or from a user.
2867146213Sglebius * Users are not so important, but try be quick for the times that it's
2868146213Sglebius * an interrupt.
2869146213Sglebius */
2870146213Sglebiusstatic __inline item_p
2871178259Smavng_alloc_item(int type, int flags)
2872146213Sglebius{
2873178259Smav	item_p item;
2874146213Sglebius
2875178259Smav	KASSERT(((type & ~NGQF_TYPE) == 0),
2876178259Smav	    ("%s: incorrect item type: %d", __func__, type));
2877146213Sglebius
2878229003Sglebius	item = uma_zalloc((type == NGQF_DATA) ? ng_qdzone : ng_qzone,
2879178259Smav	    ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
2880146281Sglebius
2881178259Smav	if (item) {
2882178259Smav		item->el_flags = type;
2883146213Sglebius#ifdef	NETGRAPH_DEBUG
2884178259Smav		mtx_lock(&ngq_mtx);
2885178259Smav		TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
2886178259Smav		allocated++;
2887178259Smav		mtx_unlock(&ngq_mtx);
2888178259Smav#endif
2889146213Sglebius	}
2890146213Sglebius
2891146213Sglebius	return (item);
2892146213Sglebius}
2893146213Sglebius
2894146213Sglebius/*
2895146213Sglebius * Release a queue entry
2896146213Sglebius */
2897146213Sglebiusvoid
2898146213Sglebiusng_free_item(item_p item)
2899146213Sglebius{
2900146213Sglebius	/*
2901146213Sglebius	 * The item may hold resources on it's own. We need to free
2902146213Sglebius	 * these before we can free the item. What they are depends upon
2903146213Sglebius	 * what kind of item it is. it is important that nodes zero
2904146213Sglebius	 * out pointers to resources that they remove from the item
2905146213Sglebius	 * or we release them again here.
2906146213Sglebius	 */
2907146213Sglebius	switch (item->el_flags & NGQF_TYPE) {
2908146213Sglebius	case NGQF_DATA:
2909146213Sglebius		/* If we have an mbuf still attached.. */
2910146213Sglebius		NG_FREE_M(_NGI_M(item));
2911146213Sglebius		break;
2912146213Sglebius	case NGQF_MESG:
2913146213Sglebius		_NGI_RETADDR(item) = 0;
2914146213Sglebius		NG_FREE_MSG(_NGI_MSG(item));
2915146213Sglebius		break;
2916146213Sglebius	case NGQF_FN:
2917172806Smav	case NGQF_FN2:
2918146213Sglebius		/* nothing to free really, */
2919146213Sglebius		_NGI_FN(item) = NULL;
2920146213Sglebius		_NGI_ARG1(item) = NULL;
2921146213Sglebius		_NGI_ARG2(item) = 0;
2922146213Sglebius		break;
2923146213Sglebius	}
2924146213Sglebius	/* If we still have a node or hook referenced... */
2925146213Sglebius	_NGI_CLR_NODE(item);
2926146213Sglebius	_NGI_CLR_HOOK(item);
2927146213Sglebius
2928146213Sglebius#ifdef	NETGRAPH_DEBUG
2929146213Sglebius	mtx_lock(&ngq_mtx);
2930146213Sglebius	TAILQ_REMOVE(&ng_itemlist, item, all);
2931146213Sglebius	allocated--;
2932146213Sglebius	mtx_unlock(&ngq_mtx);
2933146213Sglebius#endif
2934229003Sglebius	uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA) ?
2935229003Sglebius	    ng_qdzone : ng_qzone, item);
2936146213Sglebius}
2937146213Sglebius
2938178259Smav/*
2939178259Smav * Change type of the queue entry.
2940178259Smav * Possibly reallocates it from another UMA zone.
2941178259Smav */
2942178259Smavstatic __inline item_p
2943178259Smavng_realloc_item(item_p pitem, int type, int flags)
2944178259Smav{
2945178259Smav	item_p item;
2946178259Smav	int from, to;
2947178259Smav
2948178259Smav	KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__));
2949178259Smav	KASSERT(((type & ~NGQF_TYPE) == 0),
2950178259Smav	    ("%s: incorrect item type: %d", __func__, type));
2951178259Smav
2952178259Smav	from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA);
2953178259Smav	to = (type == NGQF_DATA);
2954178259Smav	if (from != to) {
2955178259Smav		/* If reallocation is required do it and copy item. */
2956178259Smav		if ((item = ng_alloc_item(type, flags)) == NULL) {
2957178259Smav			ng_free_item(pitem);
2958178259Smav			return (NULL);
2959178259Smav		}
2960178259Smav		*item = *pitem;
2961178259Smav		ng_free_item(pitem);
2962178259Smav	} else
2963178259Smav		item = pitem;
2964178259Smav	item->el_flags = (item->el_flags & ~NGQF_TYPE) | type;
2965178259Smav
2966178259Smav	return (item);
2967178259Smav}
2968178259Smav
2969146213Sglebius/************************************************************************
297052419Sjulian			Module routines
297152419Sjulian************************************************************************/
297252419Sjulian
297352419Sjulian/*
297452419Sjulian * Handle the loading/unloading of a netgraph node type module
297552419Sjulian */
297652419Sjulianint
297752419Sjulianng_mod_event(module_t mod, int event, void *data)
297852419Sjulian{
297952419Sjulian	struct ng_type *const type = data;
2980229003Sglebius	int error = 0;
298152419Sjulian
298252419Sjulian	switch (event) {
298352419Sjulian	case MOD_LOAD:
298452419Sjulian
298552419Sjulian		/* Register new netgraph node type */
2986229003Sglebius		if ((error = ng_newtype(type)) != 0)
298752419Sjulian			break;
298852419Sjulian
298952419Sjulian		/* Call type specific code */
299052419Sjulian		if (type->mod_event != NULL)
299170700Sjulian			if ((error = (*type->mod_event)(mod, event, data))) {
299272200Sbmilekic				mtx_lock(&ng_typelist_mtx);
299371603Sjulian				type->refs--;	/* undo it */
299452419Sjulian				LIST_REMOVE(type, types);
299572200Sbmilekic				mtx_unlock(&ng_typelist_mtx);
299670700Sjulian			}
299752419Sjulian		break;
299852419Sjulian
299952419Sjulian	case MOD_UNLOAD:
300071603Sjulian		if (type->refs > 1) {		/* make sure no nodes exist! */
300152419Sjulian			error = EBUSY;
300271603Sjulian		} else {
3003229003Sglebius			if (type->refs == 0) /* failed load, nothing to undo */
300471603Sjulian				break;
300552419Sjulian			if (type->mod_event != NULL) {	/* check with type */
300652419Sjulian				error = (*type->mod_event)(mod, event, data);
3007229003Sglebius				if (error != 0)	/* type refuses.. */
300852419Sjulian					break;
300952419Sjulian			}
301072200Sbmilekic			mtx_lock(&ng_typelist_mtx);
301152419Sjulian			LIST_REMOVE(type, types);
301272200Sbmilekic			mtx_unlock(&ng_typelist_mtx);
301352419Sjulian		}
301452419Sjulian		break;
301552419Sjulian
301652419Sjulian	default:
301752419Sjulian		if (type->mod_event != NULL)
301852419Sjulian			error = (*type->mod_event)(mod, event, data);
301952419Sjulian		else
3020132199Sphk			error = EOPNOTSUPP;		/* XXX ? */
302152419Sjulian		break;
302252419Sjulian	}
302352419Sjulian	return (error);
302452419Sjulian}
302552419Sjulian
3026229003Sglebius#ifdef VIMAGE
3027195837Srwatsonstatic void
3028195837Srwatsonvnet_netgraph_uninit(const void *unused __unused)
3029193731Szec{
3030207572Szec	node_p node = NULL, last_killed = NULL;
3031207572Szec	int i;
3032193731Szec
3033207572Szec	do {
3034207572Szec		/* Find a node to kill */
3035207572Szec		mtx_lock(&ng_namehash_mtx);
3036207572Szec		for (i = 0; i < NG_NAME_HASH_SIZE; i++) {
3037207572Szec			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
3038207572Szec				if (node != &ng_deadnode) {
3039207572Szec					NG_NODE_REF(node);
3040207572Szec					break;
3041207572Szec				}
3042207572Szec			}
3043207572Szec			if (node != NULL)
3044207572Szec				break;
3045207572Szec		}
3046207572Szec		mtx_unlock(&ng_namehash_mtx);
3047207572Szec
3048207572Szec		/* Attempt to kill it only if it is a regular node */
3049207572Szec		if (node != NULL) {
3050207572Szec			if (node == last_killed) {
3051207572Szec				/* This should never happen */
3052229003Sglebius				printf("ng node %s needs NGF_REALLY_DIE\n",
3053229003Sglebius				    node->nd_name);
3054207572Szec				if (node->nd_flags & NGF_REALLY_DIE)
3055207572Szec					panic("ng node %s won't die",
3056207572Szec					    node->nd_name);
3057207572Szec				node->nd_flags |= NGF_REALLY_DIE;
3058207572Szec			}
3059193731Szec			ng_rmnode(node, NULL, NULL, 0);
3060207572Szec			NG_NODE_UNREF(node);
3061207572Szec			last_killed = node;
3062193731Szec		}
3063207572Szec	} while (node != NULL);
3064193731Szec}
3065207572SzecVNET_SYSUNINIT(vnet_netgraph_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
3066195837Srwatson    vnet_netgraph_uninit, NULL);
3067193731Szec#endif /* VIMAGE */
3068193731Szec
306952419Sjulian/*
307052419Sjulian * Handle loading and unloading for this code.
307152419Sjulian * The only thing we need to link into is the NETISR strucure.
307252419Sjulian */
307352419Sjulianstatic int
307452419Sjulianngb_mod_event(module_t mod, int event, void *data)
307552419Sjulian{
3076186093Smav	struct proc *p;
3077186093Smav	struct thread *td;
3078186093Smav	int i, error = 0;
307952419Sjulian
308052419Sjulian	switch (event) {
308152419Sjulian	case MOD_LOAD:
3082146212Sglebius		/* Initialize everything. */
3083168049Swkoszek		NG_WORKLIST_LOCK_INIT();
3084123278Struckman		mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL,
3085123278Struckman		    MTX_DEF);
3086123278Struckman		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL,
3087123278Struckman		    MTX_DEF);
3088176802Smav		mtx_init(&ng_namehash_mtx, "netgraph namehash mutex", NULL,
3089176802Smav		    MTX_DEF);
3090151974Sglebius		mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL,
3091151974Sglebius		    MTX_DEF);
3092146212Sglebius#ifdef	NETGRAPH_DEBUG
3093176802Smav		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3094176802Smav		    MTX_DEF);
3095146212Sglebius		mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3096123278Struckman		    MTX_DEF);
3097146212Sglebius#endif
3098146212Sglebius		ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
3099146212Sglebius		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
3100146212Sglebius		uma_zone_set_max(ng_qzone, maxalloc);
3101229003Sglebius		ng_qdzone = uma_zcreate("NetGraph data items",
3102229003Sglebius		    sizeof(struct ng_item), NULL, NULL, NULL, NULL,
3103229003Sglebius		    UMA_ALIGN_CACHE, 0);
3104178259Smav		uma_zone_set_max(ng_qdzone, maxdata);
3105186093Smav		/* Autoconfigure number of threads. */
3106186093Smav		if (numthreads <= 0)
3107186093Smav			numthreads = mp_ncpus;
3108186093Smav		/* Create threads. */
3109186093Smav    		p = NULL; /* start with no process */
3110186093Smav		for (i = 0; i < numthreads; i++) {
3111186093Smav			if (kproc_kthread_add(ngthread, NULL, &p, &td,
3112186093Smav			    RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) {
3113186093Smav				numthreads = i;
3114186093Smav				break;
3115186093Smav			}
3116186093Smav		}
311752419Sjulian		break;
311852419Sjulian	case MOD_UNLOAD:
3119167677Srwatson		/* You can't unload it because an interface may be using it. */
312052419Sjulian		error = EBUSY;
312152419Sjulian		break;
312252419Sjulian	default:
312352419Sjulian		error = EOPNOTSUPP;
312452419Sjulian		break;
312552419Sjulian	}
312652419Sjulian	return (error);
312752419Sjulian}
312852419Sjulian
312952419Sjulianstatic moduledata_t netgraph_mod = {
313052419Sjulian	"netgraph",
313152419Sjulian	ngb_mod_event,
313252419Sjulian	(NULL)
313352419Sjulian};
3134139774SemaxDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE);
313572946SjulianSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
313672946SjulianSYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,"");
313772946SjulianSYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, "");
313852419Sjulian
313970784Sjulian#ifdef	NETGRAPH_DEBUG
314070700Sjulianvoid
314170784Sjuliandumphook (hook_p hook, char *file, int line)
314270784Sjulian{
314370784Sjulian	printf("hook: name %s, %d refs, Last touched:\n",
314470784Sjulian		_NG_HOOK_NAME(hook), hook->hk_refs);
314570784Sjulian	printf("	Last active @ %s, line %d\n",
314670784Sjulian		hook->lastfile, hook->lastline);
314770784Sjulian	if (line) {
314870784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
3149226829Sglebius#ifdef KDB
3150226829Sglebius		kdb_backtrace();
3151226829Sglebius#endif
315270784Sjulian	}
315370784Sjulian}
315470784Sjulian
315570784Sjulianvoid
315670784Sjuliandumpnode(node_p node, char *file, int line)
315770784Sjulian{
315870784Sjulian	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
315971047Sjulian		_NG_NODE_ID(node), node->nd_type->name,
316070784Sjulian		node->nd_numhooks, node->nd_flags,
316170784Sjulian		node->nd_refs, node->nd_name);
316270784Sjulian	printf("	Last active @ %s, line %d\n",
316370784Sjulian		node->lastfile, node->lastline);
316470784Sjulian	if (line) {
316570784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
3166226829Sglebius#ifdef KDB
3167226829Sglebius		kdb_backtrace();
3168226829Sglebius#endif
316970784Sjulian	}
317070784Sjulian}
317170784Sjulian
317270784Sjulianvoid
317370700Sjuliandumpitem(item_p item, char *file, int line)
317470700Sjulian{
3175146212Sglebius	printf(" ACTIVE item, last used at %s, line %d",
3176146212Sglebius		item->lastfile, item->lastline);
3177146212Sglebius	switch(item->el_flags & NGQF_TYPE) {
3178146212Sglebius	case NGQF_DATA:
3179146212Sglebius		printf(" - [data]\n");
3180146212Sglebius		break;
3181146212Sglebius	case NGQF_MESG:
3182146212Sglebius		printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
3183146212Sglebius		break;
3184146212Sglebius	case NGQF_FN:
3185172820Sru		printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3186172820Sru			_NGI_FN(item),
3187172820Sru			_NGI_NODE(item),
3188172820Sru			_NGI_HOOK(item),
3189172820Sru			item->body.fn.fn_arg1,
3190172820Sru			item->body.fn.fn_arg2,
3191172820Sru			item->body.fn.fn_arg2);
3192172820Sru		break;
3193172806Smav	case NGQF_FN2:
3194173110Smav		printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
3195172820Sru			_NGI_FN2(item),
3196149735Sglebius			_NGI_NODE(item),
3197149735Sglebius			_NGI_HOOK(item),
3198146212Sglebius			item->body.fn.fn_arg1,
3199146212Sglebius			item->body.fn.fn_arg2,
3200146212Sglebius			item->body.fn.fn_arg2);
3201146212Sglebius		break;
320252419Sjulian	}
320370784Sjulian	if (line) {
320470784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
3205149735Sglebius		if (_NGI_NODE(item)) {
320670784Sjulian			printf("node %p ([%x])\n",
3207149735Sglebius				_NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
320870784Sjulian		}
320970784Sjulian	}
321070700Sjulian}
321152419Sjulian
321270784Sjulianstatic void
321370784Sjulianng_dumpitems(void)
321470784Sjulian{
321570784Sjulian	item_p item;
321670784Sjulian	int i = 1;
321770784Sjulian	TAILQ_FOREACH(item, &ng_itemlist, all) {
321870784Sjulian		printf("[%d] ", i++);
321970784Sjulian		dumpitem(item, NULL, 0);
322070784Sjulian	}
322170784Sjulian}
322270784Sjulian
322370784Sjulianstatic void
322470784Sjulianng_dumpnodes(void)
322570784Sjulian{
322670784Sjulian	node_p node;
322770784Sjulian	int i = 1;
3228131008Srwatson	mtx_lock(&ng_nodelist_mtx);
322970784Sjulian	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
323070784Sjulian		printf("[%d] ", i++);
323170784Sjulian		dumpnode(node, NULL, 0);
323270784Sjulian	}
3233131008Srwatson	mtx_unlock(&ng_nodelist_mtx);
323470784Sjulian}
323570784Sjulian
323670784Sjulianstatic void
323770784Sjulianng_dumphooks(void)
323870784Sjulian{
323970784Sjulian	hook_p hook;
324070784Sjulian	int i = 1;
3241131008Srwatson	mtx_lock(&ng_nodelist_mtx);
324270784Sjulian	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
324370784Sjulian		printf("[%d] ", i++);
324470784Sjulian		dumphook(hook, NULL, 0);
324570784Sjulian	}
3246131008Srwatson	mtx_unlock(&ng_nodelist_mtx);
324770784Sjulian}
324870784Sjulian
324970700Sjulianstatic int
325070700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
325170700Sjulian{
325270700Sjulian	int error;
325370700Sjulian	int val;
325470700Sjulian	int i;
325570700Sjulian
325670700Sjulian	val = allocated;
325770700Sjulian	i = 1;
3258170289Sdwmalone	error = sysctl_handle_int(oidp, &val, 0, req);
325971047Sjulian	if (error != 0 || req->newptr == NULL)
326071047Sjulian		return (error);
326171047Sjulian	if (val == 42) {
326270784Sjulian		ng_dumpitems();
326370784Sjulian		ng_dumpnodes();
326470784Sjulian		ng_dumphooks();
326570700Sjulian	}
326671047Sjulian	return (0);
326752419Sjulian}
326852419Sjulian
326971047SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
327071047Sjulian    0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
327170784Sjulian#endif	/* NETGRAPH_DEBUG */
327270700Sjulian
327370700Sjulian
327470700Sjulian/***********************************************************************
327570700Sjulian* Worklist routines
327670700Sjulian**********************************************************************/
327752419Sjulian/*
327870700Sjulian * Pick a node off the list of nodes with work,
3279186093Smav * try get an item to process off it. Remove the node from the list.
328052419Sjulian */
328170700Sjulianstatic void
3282186093Smavngthread(void *arg)
328352419Sjulian{
3284177953Smav	for (;;) {
3285177953Smav		node_p  node;
328652419Sjulian
3287177953Smav		/* Get node from the worklist. */
3288168049Swkoszek		NG_WORKLIST_LOCK();
3289186093Smav		while ((node = STAILQ_FIRST(&ng_worklist)) == NULL)
3290186093Smav			NG_WORKLIST_SLEEP();
3291178228Smav		STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work);
3292168049Swkoszek		NG_WORKLIST_UNLOCK();
3293193731Szec		CURVNET_SET(node->nd_vnet);
3294154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
3295154275Sglebius		    __func__, node->nd_ID, node);
329670700Sjulian		/*
329770700Sjulian		 * We have the node. We also take over the reference
329870700Sjulian		 * that the list had on it.
329970700Sjulian		 * Now process as much as you can, until it won't
330070700Sjulian		 * let you have another item off the queue.
330170700Sjulian		 * All this time, keep the reference
330270700Sjulian		 * that lets us be sure that the node still exists.
330370700Sjulian		 * Let the reference go at the last minute.
330470700Sjulian		 */
330570700Sjulian		for (;;) {
3306177953Smav			item_p item;
3307151238Sglebius			int rw;
3308151238Sglebius
3309168049Swkoszek			NG_QUEUE_LOCK(&node->nd_input_queue);
3310178228Smav			item = ng_dequeue(node, &rw);
331170700Sjulian			if (item == NULL) {
3312178228Smav				node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ;
3313168049Swkoszek				NG_QUEUE_UNLOCK(&node->nd_input_queue);
331470700Sjulian				break; /* go look for another node */
331570700Sjulian			} else {
3316168049Swkoszek				NG_QUEUE_UNLOCK(&node->nd_input_queue);
331774078Sjulian				NGI_GET_NODE(item, node); /* zaps stored node */
3318151238Sglebius				ng_apply_item(node, item, rw);
331974078Sjulian				NG_NODE_UNREF(node);
332070700Sjulian			}
332170700Sjulian		}
332273238Sjulian		NG_NODE_UNREF(node);
3323193731Szec		CURVNET_RESTORE();
332452419Sjulian	}
332570700Sjulian}
332669922Sjulian
332772979Sjulian/*
332872979Sjulian * XXX
332972979Sjulian * It's posible that a debugging NG_NODE_REF may need
333072979Sjulian * to be outside the mutex zone
333172979Sjulian */
333270700Sjulianstatic void
3333177953Smavng_worklist_add(node_p node)
333470700Sjulian{
3335148236Sglebius
3336148266Sglebius	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3337148236Sglebius
3338178228Smav	if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) {
333969922Sjulian		/*
334070700Sjulian		 * If we are not already on the work queue,
334170700Sjulian		 * then put us on.
334269922Sjulian		 */
3343178228Smav		node->nd_input_queue.q_flags2 |= NGQ2_WORKQ;
3344229003Sglebius		NG_NODE_REF(node); /* XXX safe in mutex? */
3345168049Swkoszek		NG_WORKLIST_LOCK();
3346178228Smav		STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work);
3347168049Swkoszek		NG_WORKLIST_UNLOCK();
3348154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
3349154275Sglebius		    node->nd_ID, node);
3350186093Smav		NG_WORKLIST_WAKEUP();
3351177953Smav	} else {
3352154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
3353154275Sglebius		    __func__, node->nd_ID, node);
3354177953Smav	}
335570700Sjulian}
335670700Sjulian
335770700Sjulian
335870700Sjulian/***********************************************************************
335970700Sjulian* Externally useable functions to set up a queue item ready for sending
336070700Sjulian***********************************************************************/
336170700Sjulian
336270784Sjulian#ifdef	NETGRAPH_DEBUG
336370784Sjulian#define	ITEM_DEBUG_CHECKS						\
336470700Sjulian	do {								\
336571849Sjulian		if (NGI_NODE(item) ) {					\
336670700Sjulian			printf("item already has node");		\
3367174898Srwatson			kdb_enter(KDB_WHY_NETGRAPH, "has node");	\
336871849Sjulian			NGI_CLR_NODE(item);				\
336970700Sjulian		}							\
337071849Sjulian		if (NGI_HOOK(item) ) {					\
337170700Sjulian			printf("item already has hook");		\
3372174898Srwatson			kdb_enter(KDB_WHY_NETGRAPH, "has hook");	\
337371849Sjulian			NGI_CLR_HOOK(item);				\
337470700Sjulian		}							\
337570700Sjulian	} while (0)
337670700Sjulian#else
337770784Sjulian#define ITEM_DEBUG_CHECKS
337870700Sjulian#endif
337970700Sjulian
338070700Sjulian/*
3381131374Sjulian * Put mbuf into the item.
338270700Sjulian * Hook and node references will be removed when the item is dequeued.
338370700Sjulian * (or equivalent)
338470700Sjulian * (XXX) Unsafe because no reference held by peer on remote node.
338570700Sjulian * remote node might go away in this timescale.
338670700Sjulian * We know the hooks can't go away because that would require getting
338770700Sjulian * a writer item on both nodes and we must have at least a  reader
3388151973Sglebius * here to be able to do this.
338970700Sjulian * Note that the hook loaded is the REMOTE hook.
339070700Sjulian *
339170700Sjulian * This is possibly in the critical path for new data.
339270700Sjulian */
339370700Sjulianitem_p
3394146281Sglebiusng_package_data(struct mbuf *m, int flags)
339570700Sjulian{
339670700Sjulian	item_p item;
339770700Sjulian
3398178259Smav	if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) {
3399176849Smav		NG_FREE_M(m);
3400176849Smav		return (NULL);
3401176849Smav	}
340270784Sjulian	ITEM_DEBUG_CHECKS;
3403178259Smav	item->el_flags |= NGQF_READER;
340470700Sjulian	NGI_M(item) = m;
340570700Sjulian	return (item);
340670700Sjulian}
340770700Sjulian
340870700Sjulian/*
340970700Sjulian * Allocate a queue item and put items into it..
341070700Sjulian * Evaluate the address as this will be needed to queue it and
341170700Sjulian * to work out what some of the fields should be.
341270700Sjulian * Hook and node references will be removed when the item is dequeued.
341370700Sjulian * (or equivalent)
341470700Sjulian */
341570700Sjulianitem_p
3416146281Sglebiusng_package_msg(struct ng_mesg *msg, int flags)
341770700Sjulian{
341870700Sjulian	item_p item;
341970700Sjulian
3420178259Smav	if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) {
342171849Sjulian		NG_FREE_MSG(msg);
342270700Sjulian		return (NULL);
342369922Sjulian	}
342470784Sjulian	ITEM_DEBUG_CHECKS;
3425149505Sglebius	/* Messages items count as writers unless explicitly exempted. */
3426149505Sglebius	if (msg->header.cmd & NGM_READONLY)
3427178259Smav		item->el_flags |= NGQF_READER;
3428149505Sglebius	else
3429178259Smav		item->el_flags |= NGQF_WRITER;
343070700Sjulian	/*
343170700Sjulian	 * Set the current lasthook into the queue item
343270700Sjulian	 */
343370700Sjulian	NGI_MSG(item) = msg;
3434102244Sarchie	NGI_RETADDR(item) = 0;
343570700Sjulian	return (item);
343670700Sjulian}
343769922Sjulian
343870700Sjulian
343970700Sjulian
344071849Sjulian#define SET_RETADDR(item, here, retaddr)				\
344171047Sjulian	do {	/* Data or fn items don't have retaddrs */		\
344271047Sjulian		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
344370700Sjulian			if (retaddr) {					\
344470700Sjulian				NGI_RETADDR(item) = retaddr;		\
344570700Sjulian			} else {					\
344670700Sjulian				/*					\
344770700Sjulian				 * The old return address should be ok.	\
344870700Sjulian				 * If there isn't one, use the address	\
344970700Sjulian				 * here.				\
345070700Sjulian				 */					\
345170700Sjulian				if (NGI_RETADDR(item) == 0) {		\
345270700Sjulian					NGI_RETADDR(item)		\
345370700Sjulian						= ng_node2ID(here);	\
345470700Sjulian				}					\
345570700Sjulian			}						\
345670700Sjulian		}							\
345770700Sjulian	} while (0)
345870700Sjulian
345970700Sjulianint
346070700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
346170700Sjulian{
346271849Sjulian	hook_p peer;
346371849Sjulian	node_p peernode;
346470784Sjulian	ITEM_DEBUG_CHECKS;
346570700Sjulian	/*
346670700Sjulian	 * Quick sanity check..
346770784Sjulian	 * Since a hook holds a reference on it's node, once we know
346870784Sjulian	 * that the peer is still connected (even if invalid,) we know
346970784Sjulian	 * that the peer node is present, though maybe invalid.
347070700Sjulian	 */
3471219827Sglebius	mtx_lock(&ng_topo_mtx);
3472229003Sglebius	if ((hook == NULL) || NG_HOOK_NOT_VALID(hook) ||
3473178311Smav	    NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) ||
3474178311Smav	    NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) {
347570700Sjulian		NG_FREE_ITEM(item);
347671047Sjulian		TRAP_ERROR();
3477219827Sglebius		mtx_unlock(&ng_topo_mtx);
347873083Sjulian		return (ENETDOWN);
347952419Sjulian	}
348052419Sjulian
348170700Sjulian	/*
348270700Sjulian	 * Transfer our interest to the other (peer) end.
348370700Sjulian	 */
348471849Sjulian	NG_HOOK_REF(peer);
3485178311Smav	NG_NODE_REF(peernode);
348671849Sjulian	NGI_SET_HOOK(item, peer);
348771849Sjulian	NGI_SET_NODE(item, peernode);
348879706Sjulian	SET_RETADDR(item, here, retaddr);
3489219827Sglebius
3490219827Sglebius	mtx_unlock(&ng_topo_mtx);
3491219827Sglebius
349270700Sjulian	return (0);
349370700Sjulian}
349452419Sjulian
349570700Sjulianint
3496227130Sfjoeng_address_path(node_p here, item_p item, const char *address, ng_ID_t retaddr)
349770700Sjulian{
3498152451Sglebius	node_p	dest = NULL;
349970700Sjulian	hook_p	hook = NULL;
3500152451Sglebius	int	error;
350170700Sjulian
350270784Sjulian	ITEM_DEBUG_CHECKS;
350370700Sjulian	/*
350470700Sjulian	 * Note that ng_path2noderef increments the reference count
350570700Sjulian	 * on the node for us if it finds one. So we don't have to.
350670700Sjulian	 */
350770700Sjulian	error = ng_path2noderef(here, address, &dest, &hook);
350870700Sjulian	if (error) {
350970700Sjulian		NG_FREE_ITEM(item);
351070784Sjulian		return (error);
351152419Sjulian	}
351271849Sjulian	NGI_SET_NODE(item, dest);
3513219827Sglebius	if (hook)
351471849Sjulian		NGI_SET_HOOK(item, hook);
3515219827Sglebius
351671849Sjulian	SET_RETADDR(item, here, retaddr);
351770700Sjulian	return (0);
351870700Sjulian}
351952419Sjulian
352070700Sjulianint
352170700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
352270700Sjulian{
352370700Sjulian	node_p dest;
352470700Sjulian
352570784Sjulian	ITEM_DEBUG_CHECKS;
352670700Sjulian	/*
352770700Sjulian	 * Find the target node.
352870700Sjulian	 */
352970700Sjulian	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
353070700Sjulian	if (dest == NULL) {
353170700Sjulian		NG_FREE_ITEM(item);
353271047Sjulian		TRAP_ERROR();
353370700Sjulian		return(EINVAL);
353470700Sjulian	}
353570700Sjulian	/* Fill out the contents */
353671849Sjulian	NGI_SET_NODE(item, dest);
353771849Sjulian	NGI_CLR_HOOK(item);
353871849Sjulian	SET_RETADDR(item, here, retaddr);
353952419Sjulian	return (0);
354052419Sjulian}
354152419Sjulian
354252419Sjulian/*
354370700Sjulian * special case to send a message to self (e.g. destroy node)
354470700Sjulian * Possibly indicate an arrival hook too.
354570700Sjulian * Useful for removing that hook :-)
354652419Sjulian */
354770700Sjulianitem_p
354870700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
354952419Sjulian{
355070700Sjulian	item_p item;
355152419Sjulian
355270700Sjulian	/*
355370700Sjulian	 * Find the target node.
355470700Sjulian	 * If there is a HOOK argument, then use that in preference
355570700Sjulian	 * to the address.
355670700Sjulian	 */
3557178259Smav	if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) {
355871849Sjulian		NG_FREE_MSG(msg);
355970700Sjulian		return (NULL);
356052419Sjulian	}
356170700Sjulian
356270700Sjulian	/* Fill out the contents */
3563178259Smav	item->el_flags |= NGQF_WRITER;
356470784Sjulian	NG_NODE_REF(here);
356571849Sjulian	NGI_SET_NODE(item, here);
356671849Sjulian	if (hook) {
356770784Sjulian		NG_HOOK_REF(hook);
356871849Sjulian		NGI_SET_HOOK(item, hook);
356971849Sjulian	}
357070700Sjulian	NGI_MSG(item) = msg;
357170700Sjulian	NGI_RETADDR(item) = ng_node2ID(here);
357270700Sjulian	return (item);
357352419Sjulian}
357452419Sjulian
3575172806Smav/*
3576172806Smav * Send ng_item_fn function call to the specified node.
3577172806Smav */
3578172806Smav
3579146281Sglebiusint
3580173605Sglebiusng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
358171047Sjulian{
358271047Sjulian
3583173605Sglebius	return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
358471047Sjulian}
358571047Sjulian
3586172806Smavint
3587173605Sglebiusng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
3588172806Smav	int flags)
3589172806Smav{
3590172806Smav	item_p item;
3591172806Smav
3592178259Smav	if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) {
3593172806Smav		return (ENOMEM);
3594172806Smav	}
3595178259Smav	item->el_flags |= NGQF_WRITER;
3596172806Smav	NG_NODE_REF(node); /* and one for the item */
3597172806Smav	NGI_SET_NODE(item, node);
3598172806Smav	if (hook) {
3599172806Smav		NG_HOOK_REF(hook);
3600172806Smav		NGI_SET_HOOK(item, hook);
3601172806Smav	}
3602173605Sglebius	NGI_FN(item) = fn;
3603172806Smav	NGI_ARG1(item) = arg1;
3604172806Smav	NGI_ARG2(item) = arg2;
3605172806Smav	return(ng_snd_item(item, flags));
3606172806Smav}
3607172806Smav
3608172806Smav/*
3609173605Sglebius * Send ng_item_fn2 function call to the specified node.
3610173605Sglebius *
3611173605Sglebius * If an optional pitem parameter is supplied, its apply
3612173605Sglebius * callback will be copied to the new item. If also NG_REUSE_ITEM
3613173605Sglebius * flag is set, no new item will be allocated, but pitem will
3614173605Sglebius * be used.
3615172806Smav */
3616172806Smavint
3617173605Sglebiusng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
3618173605Sglebius	int arg2, int flags)
3619172806Smav{
3620172806Smav	item_p item;
3621172806Smav
3622173605Sglebius	KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
3623173605Sglebius	    ("%s: NG_REUSE_ITEM but no pitem", __func__));
3624172806Smav
3625173605Sglebius	/*
3626173605Sglebius	 * Allocate a new item if no supplied or
3627173605Sglebius	 * if we can't use supplied one.
3628173605Sglebius	 */
3629173605Sglebius	if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
3630178259Smav		if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL)
3631173605Sglebius			return (ENOMEM);
3632178259Smav		if (pitem != NULL)
3633178259Smav			item->apply = pitem->apply;
3634176849Smav	} else {
3635178259Smav		if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL)
3636178259Smav			return (ENOMEM);
3637176849Smav	}
3638172806Smav
3639178259Smav	item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER;
3640172806Smav	NG_NODE_REF(node); /* and one for the item */
3641172806Smav	NGI_SET_NODE(item, node);
3642172806Smav	if (hook) {
3643172806Smav		NG_HOOK_REF(hook);
3644172806Smav		NGI_SET_HOOK(item, hook);
3645172806Smav	}
3646172806Smav	NGI_FN2(item) = fn;
3647172806Smav	NGI_ARG1(item) = arg1;
3648172806Smav	NGI_ARG2(item) = arg2;
3649172806Smav	return(ng_snd_item(item, flags));
3650172806Smav}
3651172806Smav
3652172806Smav/*
365391711Sjulian * Official timeout routines for Netgraph nodes.
365491711Sjulian */
365591711Sjulianstatic void
3656140852Sglebiusng_callout_trampoline(void *arg)
365791711Sjulian{
365891711Sjulian	item_p item = arg;
365991711Sjulian
3660193731Szec	CURVNET_SET(NGI_NODE(item)->nd_vnet);
366191711Sjulian	ng_snd_item(item, 0);
3662193731Szec	CURVNET_RESTORE();
366391711Sjulian}
366491711Sjulian
366591711Sjulian
3666137138Sglebiusint
3667138268Sglebiusng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
366891711Sjulian    ng_item_fn *fn, void * arg1, int arg2)
366991711Sjulian{
3670149881Sglebius	item_p item, oitem;
367191711Sjulian
3672178259Smav	if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL)
3673137138Sglebius		return (ENOMEM);
3674137138Sglebius
3675178259Smav	item->el_flags |= NGQF_WRITER;
367691711Sjulian	NG_NODE_REF(node);		/* and one for the item */
367791711Sjulian	NGI_SET_NODE(item, node);
367891711Sjulian	if (hook) {
367991711Sjulian		NG_HOOK_REF(hook);
368091711Sjulian		NGI_SET_HOOK(item, hook);
368191711Sjulian	}
368291711Sjulian	NGI_FN(item) = fn;
368391711Sjulian	NGI_ARG1(item) = arg1;
368491711Sjulian	NGI_ARG2(item) = arg2;
3685149881Sglebius	oitem = c->c_arg;
3686149881Sglebius	if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
3687149881Sglebius	    oitem != NULL)
3688149881Sglebius		NG_FREE_ITEM(oitem);
3689137138Sglebius	return (0);
369091711Sjulian}
369191711Sjulian
369291711Sjulian/* A special modified version of untimeout() */
3693152451Sglebiusint
3694138268Sglebiusng_uncallout(struct callout *c, node_p node)
369591711Sjulian{
369691711Sjulian	item_p item;
3697137138Sglebius	int rval;
3698149357Sglebius
3699149357Sglebius	KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
3700149357Sglebius	KASSERT(node != NULL, ("ng_uncallout: NULL node"));
3701149357Sglebius
3702137230Sglebius	rval = callout_stop(c);
3703137138Sglebius	item = c->c_arg;
3704137138Sglebius	/* Do an extra check */
3705140852Sglebius	if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
3706137138Sglebius	    (NGI_NODE(item) == node)) {
370791711Sjulian		/*
370891711Sjulian		 * We successfully removed it from the queue before it ran
3709152451Sglebius		 * So now we need to unreference everything that was
371091711Sjulian		 * given extra references. (NG_FREE_ITEM does this).
371191711Sjulian		 */
371291711Sjulian		NG_FREE_ITEM(item);
371391711Sjulian	}
3714149881Sglebius	c->c_arg = NULL;
3715137138Sglebius
3716137138Sglebius	return (rval);
371791711Sjulian}
371891711Sjulian
371970700Sjulian/*
372070700Sjulian * Set the address, if none given, give the node here.
372170700Sjulian */
372270700Sjulianvoid
372370700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
372470700Sjulian{
372570700Sjulian	if (retaddr) {
372670700Sjulian		NGI_RETADDR(item) = retaddr;
372770700Sjulian	} else {
372870700Sjulian		/*
372970700Sjulian		 * The old return address should be ok.
373070700Sjulian		 * If there isn't one, use the address here.
373170700Sjulian		 */
373270700Sjulian		NGI_RETADDR(item) = ng_node2ID(here);
373370700Sjulian	}
373470700Sjulian}
373552419Sjulian
373670700Sjulian#define TESTING
373770700Sjulian#ifdef TESTING
373870700Sjulian/* just test all the macros */
373970700Sjulianvoid
374070700Sjulianng_macro_test(item_p item);
374170700Sjulianvoid
374270700Sjulianng_macro_test(item_p item)
374370700Sjulian{
374470700Sjulian	node_p node = NULL;
374570700Sjulian	hook_p hook = NULL;
374670700Sjulian	struct mbuf *m;
374770700Sjulian	struct ng_mesg *msg;
374870700Sjulian	ng_ID_t retaddr;
374970700Sjulian	int	error;
375070700Sjulian
375170700Sjulian	NGI_GET_M(item, m);
375270700Sjulian	NGI_GET_MSG(item, msg);
375370700Sjulian	retaddr = NGI_RETADDR(item);
3754131374Sjulian	NG_SEND_DATA(error, hook, m, NULL);
375570700Sjulian	NG_SEND_DATA_ONLY(error, hook, m);
375670700Sjulian	NG_FWD_NEW_DATA(error, item, hook, m);
375770784Sjulian	NG_FWD_ITEM_HOOK(error, item, hook);
375870700Sjulian	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
375970700Sjulian	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
376070700Sjulian	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
376170700Sjulian	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
376270700Sjulian}
376370700Sjulian#endif /* TESTING */
3764