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: stable/11/sys/netgraph/ng_base.c 369960 2021-06-15 16:57:32Z git2svn $
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>
46231766Sglebius#include <sys/systm.h>
47139235Sglebius#include <sys/ctype.h>
48231831Sglebius#include <sys/hash.h>
49131933Smarcel#include <sys/kdb.h>
5052419Sjulian#include <sys/kernel.h>
51231760Sglebius#include <sys/kthread.h>
52154225Sglebius#include <sys/ktr.h>
53114216Skan#include <sys/limits.h>
54230480Sglebius#include <sys/lock.h>
5552419Sjulian#include <sys/malloc.h>
56139235Sglebius#include <sys/mbuf.h>
57231760Sglebius#include <sys/proc.h>
5852419Sjulian#include <sys/queue.h>
59231760Sglebius#include <sys/refcount.h>
60231760Sglebius#include <sys/rwlock.h>
61231760Sglebius#include <sys/smp.h>
6272946Sjulian#include <sys/sysctl.h>
63139235Sglebius#include <sys/syslog.h>
64186093Smav#include <sys/unistd.h>
65177953Smav#include <machine/cpu.h>
66295126Sglebius#include <vm/uma.h>
6752419Sjulian
6852419Sjulian#include <net/netisr.h>
69195699Srwatson#include <net/vnet.h>
7052419Sjulian
7152419Sjulian#include <netgraph/ng_message.h>
7252419Sjulian#include <netgraph/netgraph.h>
7353913Sarchie#include <netgraph/ng_parse.h>
7452419Sjulian
7572053SjulianMODULE_VERSION(netgraph, NG_ABI_VERSION);
7659756Speter
77151974Sglebius/* Mutex to protect topology events. */
78256550Smelifarostatic struct rwlock	ng_topo_lock;
79256550Smelifaro#define	TOPOLOGY_RLOCK()	rw_rlock(&ng_topo_lock)
80256550Smelifaro#define	TOPOLOGY_RUNLOCK()	rw_runlock(&ng_topo_lock)
81256550Smelifaro#define	TOPOLOGY_WLOCK()	rw_wlock(&ng_topo_lock)
82256550Smelifaro#define	TOPOLOGY_WUNLOCK()	rw_wunlock(&ng_topo_lock)
83256550Smelifaro#define	TOPOLOGY_NOTOWNED()	rw_assert(&ng_topo_lock, RA_UNLOCKED)
84151974Sglebius
8570784Sjulian#ifdef	NETGRAPH_DEBUG
86176802Smavstatic struct mtx	ng_nodelist_mtx; /* protects global node/hook lists */
87152451Sglebiusstatic struct mtx	ngq_mtx;	/* protects the queue item list */
8870784Sjulian
8970784Sjulianstatic SLIST_HEAD(, ng_node) ng_allnodes;
9070784Sjulianstatic LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
9170784Sjulianstatic SLIST_HEAD(, ng_hook) ng_allhooks;
9270784Sjulianstatic LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
9370784Sjulian
9470784Sjulianstatic void ng_dumpitems(void);
9570784Sjulianstatic void ng_dumpnodes(void);
9670784Sjulianstatic void ng_dumphooks(void);
9770784Sjulian
9870784Sjulian#endif	/* NETGRAPH_DEBUG */
9970935Sjulian/*
100152451Sglebius * DEAD versions of the structures.
101249583Sgabor * In order to avoid races, it is sometimes necessary to point
102152451Sglebius * at SOMETHING even though theoretically, the current entity is
10370935Sjulian * INVALID. Use these to avoid these races.
10470935Sjulian */
10570935Sjulianstruct ng_type ng_deadtype = {
10670935Sjulian	NG_ABI_VERSION,
10770935Sjulian	"dead",
10870935Sjulian	NULL,	/* modevent */
10970935Sjulian	NULL,	/* constructor */
11070935Sjulian	NULL,	/* rcvmsg */
11170935Sjulian	NULL,	/* shutdown */
11270935Sjulian	NULL,	/* newhook */
11370935Sjulian	NULL,	/* findhook */
11470935Sjulian	NULL,	/* connect */
11570935Sjulian	NULL,	/* rcvdata */
11670935Sjulian	NULL,	/* disconnect */
11770935Sjulian	NULL, 	/* cmdlist */
11870935Sjulian};
11970784Sjulian
12070935Sjulianstruct ng_node ng_deadnode = {
12170935Sjulian	"dead",
12270935Sjulian	&ng_deadtype,
123132464Sjulian	NGF_INVALID,
12470935Sjulian	0,	/* numhooks */
12570935Sjulian	NULL,	/* private */
12670935Sjulian	0,	/* ID */
127201145Santoine	LIST_HEAD_INITIALIZER(ng_deadnode.nd_hooks),
12870935Sjulian	{},	/* all_nodes list entry */
12970935Sjulian	{},	/* id hashtable list entry */
13070935Sjulian	{	0,
131178228Smav		0,
13270935Sjulian		{}, /* should never use! (should hang) */
133178228Smav		{}, /* workqueue entry */
134178228Smav		STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue),
13570935Sjulian	},
136178228Smav	1,	/* refs */
137191827Szec	NULL,	/* vnet */
13870935Sjulian#ifdef	NETGRAPH_DEBUG
13970935Sjulian	ND_MAGIC,
14070935Sjulian	__FILE__,
14170935Sjulian	__LINE__,
14270935Sjulian	{NULL}
14370935Sjulian#endif	/* NETGRAPH_DEBUG */
14470935Sjulian};
14570935Sjulian
14670935Sjulianstruct ng_hook ng_deadhook = {
14770935Sjulian	"dead",
14870935Sjulian	NULL,		/* private */
14970935Sjulian	HK_INVALID | HK_DEAD,
150148261Sglebius	0,		/* undefined data link type */
15170935Sjulian	&ng_deadhook,	/* Peer is self */
15270935Sjulian	&ng_deadnode,	/* attached to deadnode */
15370935Sjulian	{},		/* hooks list */
15471885Sjulian	NULL,		/* override rcvmsg() */
15571885Sjulian	NULL,		/* override rcvdata() */
156178228Smav	1,		/* refs always >= 1 */
15770935Sjulian#ifdef	NETGRAPH_DEBUG
15870935Sjulian	HK_MAGIC,
15970935Sjulian	__FILE__,
16070935Sjulian	__LINE__,
16170935Sjulian	{NULL}
16270935Sjulian#endif	/* NETGRAPH_DEBUG */
16370935Sjulian};
16470935Sjulian
16570935Sjulian/*
16670935Sjulian * END DEAD STRUCTURES
16770935Sjulian */
16870700Sjulian/* List nodes with unallocated work */
169178228Smavstatic STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist);
17071902Sjulianstatic struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
17170700Sjulian
17252419Sjulian/* List of installed types */
17370700Sjulianstatic LIST_HEAD(, ng_type) ng_typelist;
174230480Sglebiusstatic struct rwlock	ng_typelist_lock;
175230480Sglebius#define	TYPELIST_RLOCK()	rw_rlock(&ng_typelist_lock)
176230480Sglebius#define	TYPELIST_RUNLOCK()	rw_runlock(&ng_typelist_lock)
177230480Sglebius#define	TYPELIST_WLOCK()	rw_wlock(&ng_typelist_lock)
178230480Sglebius#define	TYPELIST_WUNLOCK()	rw_wunlock(&ng_typelist_lock)
17952419Sjulian
180231831Sglebius/* Hash related definitions. */
181231831SglebiusLIST_HEAD(nodehash, ng_node);
182231831Sglebiusstatic VNET_DEFINE(struct nodehash *, ng_ID_hash);
183231831Sglebiusstatic VNET_DEFINE(u_long, ng_ID_hmask);
184231831Sglebiusstatic VNET_DEFINE(u_long, ng_nodes);
185231831Sglebiusstatic VNET_DEFINE(struct nodehash *, ng_name_hash);
186231831Sglebiusstatic VNET_DEFINE(u_long, ng_name_hmask);
187231831Sglebiusstatic VNET_DEFINE(u_long, ng_named_nodes);
188231831Sglebius#define	V_ng_ID_hash		VNET(ng_ID_hash)
189231831Sglebius#define	V_ng_ID_hmask		VNET(ng_ID_hmask)
190231831Sglebius#define	V_ng_nodes		VNET(ng_nodes)
191231831Sglebius#define	V_ng_name_hash		VNET(ng_name_hash)
192231831Sglebius#define	V_ng_name_hmask		VNET(ng_name_hmask)
193231831Sglebius#define	V_ng_named_nodes	VNET(ng_named_nodes)
194195699Srwatson
195230480Sglebiusstatic struct rwlock	ng_idhash_lock;
196230480Sglebius#define	IDHASH_RLOCK()		rw_rlock(&ng_idhash_lock)
197230480Sglebius#define	IDHASH_RUNLOCK()	rw_runlock(&ng_idhash_lock)
198230480Sglebius#define	IDHASH_WLOCK()		rw_wlock(&ng_idhash_lock)
199230480Sglebius#define	IDHASH_WUNLOCK()	rw_wunlock(&ng_idhash_lock)
200230480Sglebius
20171354Sjulian/* Method to find a node.. used twice so do it here */
202231831Sglebius#define NG_IDHASH_FN(ID) ((ID) % (V_ng_ID_hmask + 1))
20371354Sjulian#define NG_IDHASH_FIND(ID, node)					\
20471354Sjulian	do { 								\
205230480Sglebius		rw_assert(&ng_idhash_lock, RA_LOCKED);			\
206181803Sbz		LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)],	\
20771354Sjulian						nd_idnodes) {		\
20871354Sjulian			if (NG_NODE_IS_VALID(node)			\
20971354Sjulian			&& (NG_NODE_ID(node) == ID)) {			\
21071354Sjulian				break;					\
21171354Sjulian			}						\
21271354Sjulian		}							\
21371354Sjulian	} while (0)
21452722Sjulian
215230480Sglebiusstatic struct rwlock	ng_namehash_lock;
216230480Sglebius#define	NAMEHASH_RLOCK()	rw_rlock(&ng_namehash_lock)
217230480Sglebius#define	NAMEHASH_RUNLOCK()	rw_runlock(&ng_namehash_lock)
218230480Sglebius#define	NAMEHASH_WLOCK()	rw_wlock(&ng_namehash_lock)
219230480Sglebius#define	NAMEHASH_WUNLOCK()	rw_wunlock(&ng_namehash_lock)
220176802Smav
22152419Sjulian/* Internal functions */
22252419Sjulianstatic int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
22370700Sjulianstatic int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
22452722Sjulianstatic ng_ID_t	ng_decodeidname(const char *name);
22552419Sjulianstatic int	ngb_mod_event(module_t mod, int event, void *data);
226177953Smavstatic void	ng_worklist_add(node_p node);
227186093Smavstatic void	ngthread(void *);
228170180Sglebiusstatic int	ng_apply_item(node_p node, item_p item, int rw);
229178228Smavstatic void	ng_flush_input_queue(node_p node);
23070700Sjulianstatic node_p	ng_ID2noderef(ng_ID_t ID);
231172806Smavstatic int	ng_con_nodes(item_p item, node_p node, const char *name,
232172806Smav		    node_p node2, const char *name2);
233172806Smavstatic int	ng_con_part2(node_p node, item_p item, hook_p hook);
234172806Smavstatic int	ng_con_part3(node_p node, item_p item, hook_p hook);
235231831Sglebiusstatic int	ng_mkpeer(node_p node, const char *name, const char *name2,
236231831Sglebius		    char *type);
237231831Sglebiusstatic void	ng_name_rehash(void);
238231831Sglebiusstatic void	ng_ID_rehash(void);
23952419Sjulian
240152451Sglebius/* Imported, these used to be externally visible, some may go back. */
24170700Sjulianvoid	ng_destroy_hook(hook_p hook);
24270700Sjulianint	ng_path2noderef(node_p here, const char *path,
24370700Sjulian	node_p *dest, hook_p *lasthook);
24470700Sjulianint	ng_make_node(const char *type, node_p *nodepp);
24570700Sjulianint	ng_path_parse(char *addr, char **node, char **path, char **hook);
24671849Sjulianvoid	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
24770784Sjulianvoid	ng_unname(node_p node);
24870700Sjulian
24952419Sjulian/* Our own netgraph malloc type */
25052419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
25170700SjulianMALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
252227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook",
253227293Sed    "netgraph hook structures");
254227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node",
255227293Sed    "netgraph node structures");
256227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item",
257227293Sed    "netgraph item structures");
25852419Sjulian
25970700Sjulian/* Should not be visible outside this file */
26070784Sjulian
26170784Sjulian#define _NG_ALLOC_HOOK(hook) \
262184205Sdes	hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
26370784Sjulian#define _NG_ALLOC_NODE(node) \
264184205Sdes	node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
26570784Sjulian
266168049Swkoszek#define	NG_QUEUE_LOCK_INIT(n)			\
267168137Swkoszek	mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF)
268168049Swkoszek#define	NG_QUEUE_LOCK(n)			\
269168137Swkoszek	mtx_lock(&(n)->q_mtx)
270168049Swkoszek#define	NG_QUEUE_UNLOCK(n)			\
271168137Swkoszek	mtx_unlock(&(n)->q_mtx)
272168049Swkoszek#define	NG_WORKLIST_LOCK_INIT()			\
273168137Swkoszek	mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF)
274168049Swkoszek#define	NG_WORKLIST_LOCK()			\
275168137Swkoszek	mtx_lock(&ng_worklist_mtx)
276168049Swkoszek#define	NG_WORKLIST_UNLOCK()			\
277168137Swkoszek	mtx_unlock(&ng_worklist_mtx)
278186093Smav#define	NG_WORKLIST_SLEEP()			\
279186093Smav	mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0)
280186093Smav#define	NG_WORKLIST_WAKEUP()			\
281186093Smav	wakeup_one(&ng_worklist)
282168049Swkoszek
28370784Sjulian#ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
28470784Sjulian/*
28570784Sjulian * In debug mode:
28670784Sjulian * In an attempt to help track reference count screwups
28770784Sjulian * we do not free objects back to the malloc system, but keep them
28870784Sjulian * in a local cache where we can examine them and keep information safely
28970784Sjulian * after they have been freed.
29070784Sjulian * We use this scheme for nodes and hooks, and to some extent for items.
29170784Sjulian */
29270784Sjulianstatic __inline hook_p
29370784Sjulianng_alloc_hook(void)
29470784Sjulian{
29570784Sjulian	hook_p hook;
29670784Sjulian	SLIST_ENTRY(ng_hook) temp;
29772200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
29870784Sjulian	hook = LIST_FIRST(&ng_freehooks);
29970784Sjulian	if (hook) {
30070784Sjulian		LIST_REMOVE(hook, hk_hooks);
30170784Sjulian		bcopy(&hook->hk_all, &temp, sizeof(temp));
30270784Sjulian		bzero(hook, sizeof(struct ng_hook));
30370784Sjulian		bcopy(&temp, &hook->hk_all, sizeof(temp));
30472200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
30570784Sjulian		hook->hk_magic = HK_MAGIC;
30670784Sjulian	} else {
30772200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
30870784Sjulian		_NG_ALLOC_HOOK(hook);
30970784Sjulian		if (hook) {
31070784Sjulian			hook->hk_magic = HK_MAGIC;
31172200Sbmilekic			mtx_lock(&ng_nodelist_mtx);
31270784Sjulian			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
31372200Sbmilekic			mtx_unlock(&ng_nodelist_mtx);
31470784Sjulian		}
31570784Sjulian	}
31670784Sjulian	return (hook);
31770784Sjulian}
31870784Sjulian
31970784Sjulianstatic __inline node_p
32070784Sjulianng_alloc_node(void)
32170784Sjulian{
32270784Sjulian	node_p node;
32370784Sjulian	SLIST_ENTRY(ng_node) temp;
32472200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
32570784Sjulian	node = LIST_FIRST(&ng_freenodes);
32670784Sjulian	if (node) {
32770784Sjulian		LIST_REMOVE(node, nd_nodes);
32870784Sjulian		bcopy(&node->nd_all, &temp, sizeof(temp));
32970784Sjulian		bzero(node, sizeof(struct ng_node));
33070784Sjulian		bcopy(&temp, &node->nd_all, sizeof(temp));
33172200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
33270784Sjulian		node->nd_magic = ND_MAGIC;
33370784Sjulian	} else {
33472200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
33570784Sjulian		_NG_ALLOC_NODE(node);
33670784Sjulian		if (node) {
33770784Sjulian			node->nd_magic = ND_MAGIC;
33872200Sbmilekic			mtx_lock(&ng_nodelist_mtx);
33970784Sjulian			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
34072200Sbmilekic			mtx_unlock(&ng_nodelist_mtx);
34170784Sjulian		}
34270784Sjulian	}
34370784Sjulian	return (node);
34470784Sjulian}
34570784Sjulian
34670784Sjulian#define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
34770784Sjulian#define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
34870784Sjulian
34970784Sjulian#define NG_FREE_HOOK(hook)						\
35070784Sjulian	do {								\
351229003Sglebius		mtx_lock(&ng_nodelist_mtx);				\
35270784Sjulian		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
35370784Sjulian		hook->hk_magic = 0;					\
354229003Sglebius		mtx_unlock(&ng_nodelist_mtx);				\
35570784Sjulian	} while (0)
35670784Sjulian
35770784Sjulian#define NG_FREE_NODE(node)						\
35870784Sjulian	do {								\
359229003Sglebius		mtx_lock(&ng_nodelist_mtx);				\
36070784Sjulian		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
36170784Sjulian		node->nd_magic = 0;					\
362229003Sglebius		mtx_unlock(&ng_nodelist_mtx);				\
36370784Sjulian	} while (0)
36470784Sjulian
36570784Sjulian#else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
36670784Sjulian
36770784Sjulian#define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
36870784Sjulian#define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
36970784Sjulian
370184205Sdes#define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0)
371184205Sdes#define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0)
37270784Sjulian
37370784Sjulian#endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
37470784Sjulian
375131933Smarcel/* Set this to kdb_enter("X") to catch all errors as they occur */
37652419Sjulian#ifndef TRAP_ERROR
37771047Sjulian#define TRAP_ERROR()
37852419Sjulian#endif
37952419Sjulian
380215701Sdimstatic VNET_DEFINE(ng_ID_t, nextID) = 1;
381195727Srwatson#define	V_nextID			VNET(nextID)
38252722Sjulian
38353403Sarchie#ifdef INVARIANTS
38453403Sarchie#define CHECK_DATA_MBUF(m)	do {					\
38553403Sarchie		struct mbuf *n;						\
38653403Sarchie		int total;						\
38753403Sarchie									\
388113255Sdes		M_ASSERTPKTHDR(m);					\
389149818Sglebius		for (total = 0, n = (m); n != NULL; n = n->m_next) {	\
39053403Sarchie			total += n->m_len;				\
391149818Sglebius			if (n->m_nextpkt != NULL)			\
392149818Sglebius				panic("%s: m_nextpkt", __func__);	\
393149818Sglebius		}							\
394149827Sglebius									\
39553403Sarchie		if ((m)->m_pkthdr.len != total) {			\
39653403Sarchie			panic("%s: %d != %d",				\
39787599Sobrien			    __func__, (m)->m_pkthdr.len, total);	\
39853403Sarchie		}							\
39953403Sarchie	} while (0)
40053403Sarchie#else
40153403Sarchie#define CHECK_DATA_MBUF(m)
40253403Sarchie#endif
40352722Sjulian
404172806Smav#define ERROUT(x)	do { error = (x); goto done; } while (0)
40553403Sarchie
40652419Sjulian/************************************************************************
40753913Sarchie	Parse type definitions for generic messages
40853913Sarchie************************************************************************/
40953913Sarchie
41053913Sarchie/* Handy structure parse type defining macro */
41153913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
41297685Sarchiestatic const struct ng_parse_struct_field				\
41397685Sarchie	ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args;	\
41453913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
41553913Sarchie	&ng_parse_struct_type,						\
41697685Sarchie	&ng_ ## lo ## _type_fields					\
41753913Sarchie}
41853913Sarchie
41953913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
42053913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
42153913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
42253913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
42353913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
42453913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
42553913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
42653913Sarchie
42753913Sarchie/* Get length of an array when the length is stored as a 32 bit
42872645Sasmodai   value immediately preceding the array -- as with struct namelist
42953913Sarchie   and struct typelist. */
43053913Sarchiestatic int
43153913Sarchieng_generic_list_getLength(const struct ng_parse_type *type,
43253913Sarchie	const u_char *start, const u_char *buf)
43353913Sarchie{
43453913Sarchie	return *((const u_int32_t *)(buf - 4));
43553913Sarchie}
43653913Sarchie
43753913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */
43853913Sarchiestatic int
43953913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type,
44053913Sarchie	const u_char *start, const u_char *buf)
44153913Sarchie{
44253913Sarchie	const struct hooklist *hl = (const struct hooklist *)start;
44353913Sarchie
44453913Sarchie	return hl->nodeinfo.hooks;
44553913Sarchie}
44653913Sarchie
44753913Sarchie/* Array type for a variable length array of struct namelist */
44853913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
44953913Sarchie	&ng_generic_nodeinfo_type,
45053913Sarchie	&ng_generic_list_getLength
45153913Sarchie};
45253913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = {
45353913Sarchie	&ng_parse_array_type,
45453913Sarchie	&ng_nodeinfoarray_type_info
45553913Sarchie};
45653913Sarchie
45753913Sarchie/* Array type for a variable length array of struct typelist */
45853913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = {
45953913Sarchie	&ng_generic_typeinfo_type,
46053913Sarchie	&ng_generic_list_getLength
46153913Sarchie};
46253913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = {
46353913Sarchie	&ng_parse_array_type,
46453913Sarchie	&ng_typeinfoarray_type_info
46553913Sarchie};
46653913Sarchie
46753913Sarchie/* Array type for array of struct linkinfo in struct hooklist */
46853913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
46953913Sarchie	&ng_generic_linkinfo_type,
47053913Sarchie	&ng_generic_linkinfo_getLength
47153913Sarchie};
47253913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = {
47353913Sarchie	&ng_parse_array_type,
47453913Sarchie	&ng_generic_linkinfo_array_type_info
47553913Sarchie};
47653913Sarchie
477260046SglebiusDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_typeinfoarray_type));
47853913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
47953913Sarchie	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
48053913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
48153913Sarchie	(&ng_generic_nodeinfoarray_type));
48253913Sarchie
48353913Sarchie/* List of commands and how to convert arguments to/from ASCII */
48453913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = {
48553913Sarchie	{
48653913Sarchie	  NGM_GENERIC_COOKIE,
48753913Sarchie	  NGM_SHUTDOWN,
48853913Sarchie	  "shutdown",
48953913Sarchie	  NULL,
49053913Sarchie	  NULL
49153913Sarchie	},
49253913Sarchie	{
49353913Sarchie	  NGM_GENERIC_COOKIE,
49453913Sarchie	  NGM_MKPEER,
49553913Sarchie	  "mkpeer",
49653913Sarchie	  &ng_generic_mkpeer_type,
49753913Sarchie	  NULL
49853913Sarchie	},
49953913Sarchie	{
50053913Sarchie	  NGM_GENERIC_COOKIE,
50153913Sarchie	  NGM_CONNECT,
50253913Sarchie	  "connect",
50353913Sarchie	  &ng_generic_connect_type,
50453913Sarchie	  NULL
50553913Sarchie	},
50653913Sarchie	{
50753913Sarchie	  NGM_GENERIC_COOKIE,
50853913Sarchie	  NGM_NAME,
50953913Sarchie	  "name",
51053913Sarchie	  &ng_generic_name_type,
51153913Sarchie	  NULL
51253913Sarchie	},
51353913Sarchie	{
51453913Sarchie	  NGM_GENERIC_COOKIE,
51553913Sarchie	  NGM_RMHOOK,
51653913Sarchie	  "rmhook",
51753913Sarchie	  &ng_generic_rmhook_type,
51853913Sarchie	  NULL
51953913Sarchie	},
52053913Sarchie	{
52153913Sarchie	  NGM_GENERIC_COOKIE,
52253913Sarchie	  NGM_NODEINFO,
52353913Sarchie	  "nodeinfo",
52453913Sarchie	  NULL,
52553913Sarchie	  &ng_generic_nodeinfo_type
52653913Sarchie	},
52753913Sarchie	{
52853913Sarchie	  NGM_GENERIC_COOKIE,
52953913Sarchie	  NGM_LISTHOOKS,
53053913Sarchie	  "listhooks",
53153913Sarchie	  NULL,
53253913Sarchie	  &ng_generic_hooklist_type
53353913Sarchie	},
53453913Sarchie	{
53553913Sarchie	  NGM_GENERIC_COOKIE,
53653913Sarchie	  NGM_LISTNAMES,
53753913Sarchie	  "listnames",
53853913Sarchie	  NULL,
53953913Sarchie	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
54053913Sarchie	},
54153913Sarchie	{
54253913Sarchie	  NGM_GENERIC_COOKIE,
54353913Sarchie	  NGM_LISTNODES,
54453913Sarchie	  "listnodes",
54553913Sarchie	  NULL,
54653913Sarchie	  &ng_generic_listnodes_type
54753913Sarchie	},
54853913Sarchie	{
54953913Sarchie	  NGM_GENERIC_COOKIE,
55053913Sarchie	  NGM_LISTTYPES,
55153913Sarchie	  "listtypes",
55253913Sarchie	  NULL,
553260046Sglebius	  &ng_generic_typelist_type
55453913Sarchie	},
55553913Sarchie	{
55653913Sarchie	  NGM_GENERIC_COOKIE,
55762471Sphk	  NGM_TEXT_CONFIG,
55862471Sphk	  "textconfig",
55962471Sphk	  NULL,
56062471Sphk	  &ng_parse_string_type
56162471Sphk	},
56262471Sphk	{
56362471Sphk	  NGM_GENERIC_COOKIE,
56453913Sarchie	  NGM_TEXT_STATUS,
56553913Sarchie	  "textstatus",
56653913Sarchie	  NULL,
56753913Sarchie	  &ng_parse_string_type
56853913Sarchie	},
56953913Sarchie	{
57053913Sarchie	  NGM_GENERIC_COOKIE,
57153913Sarchie	  NGM_ASCII2BINARY,
57253913Sarchie	  "ascii2binary",
57353913Sarchie	  &ng_parse_ng_mesg_type,
57453913Sarchie	  &ng_parse_ng_mesg_type
57553913Sarchie	},
57653913Sarchie	{
57753913Sarchie	  NGM_GENERIC_COOKIE,
57853913Sarchie	  NGM_BINARY2ASCII,
57953913Sarchie	  "binary2ascii",
58053913Sarchie	  &ng_parse_ng_mesg_type,
58153913Sarchie	  &ng_parse_ng_mesg_type
58253913Sarchie	},
58353913Sarchie	{ 0 }
58453913Sarchie};
58553913Sarchie
58653913Sarchie/************************************************************************
58752419Sjulian			Node routines
58852419Sjulian************************************************************************/
58952419Sjulian
59052419Sjulian/*
59152419Sjulian * Instantiate a node of the requested type
59252419Sjulian */
59352419Sjulianint
59452419Sjulianng_make_node(const char *typename, node_p *nodepp)
59552419Sjulian{
59652419Sjulian	struct ng_type *type;
59770700Sjulian	int	error;
59852419Sjulian
59952419Sjulian	/* Check that the type makes sense */
60052419Sjulian	if (typename == NULL) {
60171047Sjulian		TRAP_ERROR();
60252419Sjulian		return (EINVAL);
60352419Sjulian	}
60452419Sjulian
605132705Sglebius	/* Locate the node type. If we fail we return. Do not try to load
606132705Sglebius	 * module.
607132705Sglebius	 */
608132705Sglebius	if ((type = ng_findtype(typename)) == NULL)
609132705Sglebius		return (ENXIO);
61052419Sjulian
61170700Sjulian	/*
61270700Sjulian	 * If we have a constructor, then make the node and
61370700Sjulian	 * call the constructor to do type specific initialisation.
61470700Sjulian	 */
61570700Sjulian	if (type->constructor != NULL) {
61670700Sjulian		if ((error = ng_make_node_common(type, nodepp)) == 0) {
617220745Sglebius			if ((error = ((*type->constructor)(*nodepp))) != 0) {
61870784Sjulian				NG_NODE_UNREF(*nodepp);
61970700Sjulian			}
62070700Sjulian		}
62170700Sjulian	} else {
62270700Sjulian		/*
62370700Sjulian		 * Node has no constructor. We cannot ask for one
624167677Srwatson		 * to be made. It must be brought into existence by
62570935Sjulian		 * some external agency. The external agency should
62670700Sjulian		 * call ng_make_node_common() directly to get the
62770700Sjulian		 * netgraph part initialised.
62870700Sjulian		 */
62971047Sjulian		TRAP_ERROR();
63070700Sjulian		error = EINVAL;
63170700Sjulian	}
63270700Sjulian	return (error);
63352419Sjulian}
63452419Sjulian
63552419Sjulian/*
63670700Sjulian * Generic node creation. Called by node initialisation for externally
63770700Sjulian * instantiated nodes (e.g. hardware, sockets, etc ).
63852419Sjulian * The returned node has a reference count of 1.
63952419Sjulian */
64052419Sjulianint
64152419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp)
64252419Sjulian{
64352419Sjulian	node_p node;
64452419Sjulian
64552419Sjulian	/* Require the node type to have been already installed */
64652419Sjulian	if (ng_findtype(type->name) == NULL) {
64771047Sjulian		TRAP_ERROR();
64852419Sjulian		return (EINVAL);
64952419Sjulian	}
65052419Sjulian
65152419Sjulian	/* Make a node and try attach it to the type */
65270784Sjulian	NG_ALLOC_NODE(node);
65352419Sjulian	if (node == NULL) {
65471047Sjulian		TRAP_ERROR();
65552419Sjulian		return (ENOMEM);
65652419Sjulian	}
65770784Sjulian	node->nd_type = type;
658193731Szec#ifdef VIMAGE
659193731Szec	node->nd_vnet = curvnet;
660193731Szec#endif
66170784Sjulian	NG_NODE_REF(node);				/* note reference */
66252419Sjulian	type->refs++;
66352419Sjulian
664168049Swkoszek	NG_QUEUE_LOCK_INIT(&node->nd_input_queue);
665178228Smav	STAILQ_INIT(&node->nd_input_queue.queue);
66670784Sjulian	node->nd_input_queue.q_flags = 0;
66752419Sjulian
66852419Sjulian	/* Initialize hook list for new node */
66970784Sjulian	LIST_INIT(&node->nd_hooks);
67052419Sjulian
671231831Sglebius	/* Get an ID and put us in the hash chain. */
672230480Sglebius	IDHASH_WLOCK();
67370784Sjulian	for (;;) { /* wrap protection, even if silly */
67470700Sjulian		node_p node2 = NULL;
675181887Sjulian		node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */
67671354Sjulian
67770784Sjulian		/* Is there a problem with the new number? */
67871354Sjulian		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
67971354Sjulian		if ((node->nd_ID != 0) && (node2 == NULL)) {
68070784Sjulian			break;
68170700Sjulian		}
68270784Sjulian	}
683231831Sglebius	V_ng_nodes++;
684231831Sglebius	if (V_ng_nodes * 2 > V_ng_ID_hmask)
685231831Sglebius		ng_ID_rehash();
686229003Sglebius	LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], node,
687229003Sglebius	    nd_idnodes);
688230480Sglebius	IDHASH_WUNLOCK();
68952722Sjulian
69052419Sjulian	/* Done */
69152419Sjulian	*nodepp = node;
69252419Sjulian	return (0);
69352419Sjulian}
69452419Sjulian
69552419Sjulian/*
69652419Sjulian * Forceably start the shutdown process on a node. Either call
697167677Srwatson * its shutdown method, or do the default shutdown if there is
69852419Sjulian * no type-specific method.
69952419Sjulian *
700167677Srwatson * We can only be called from a shutdown message, so we know we have
70170939Sjulian * a writer lock, and therefore exclusive access. It also means
70270939Sjulian * that we should not be on the work queue, but we check anyhow.
70370700Sjulian *
70470700Sjulian * Persistent node types must have a type-specific method which
705167677Srwatson * allocates a new node in which case, this one is irretrievably going away,
70670939Sjulian * or cleans up anything it needs, and just makes the node valid again,
707152451Sglebius * in which case we allow the node to survive.
70870939Sjulian *
709167677Srwatson * XXX We need to think of how to tell a persistent node that we
71070939Sjulian * REALLY need to go away because the hardware has gone or we
71170939Sjulian * are rebooting.... etc.
71252419Sjulian */
71352419Sjulianvoid
71471849Sjulianng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
71552419Sjulian{
71670939Sjulian	hook_p hook;
71770939Sjulian
71852419Sjulian	/* Check if it's already shutting down */
719132464Sjulian	if ((node->nd_flags & NGF_CLOSING) != 0)
72052419Sjulian		return;
72152419Sjulian
72271849Sjulian	if (node == &ng_deadnode) {
72371849Sjulian		printf ("shutdown called on deadnode\n");
72471849Sjulian		return;
72571849Sjulian	}
72671849Sjulian
72752419Sjulian	/* Add an extra reference so it doesn't go away during this */
72870784Sjulian	NG_NODE_REF(node);
72952419Sjulian
73070784Sjulian	/*
73170784Sjulian	 * Mark it invalid so any newcomers know not to try use it
73270784Sjulian	 * Also add our own mark so we can't recurse
733132464Sjulian	 * note that NGF_INVALID does not do this as it's also set during
73470784Sjulian	 * creation
73570784Sjulian	 */
736132464Sjulian	node->nd_flags |= NGF_INVALID|NGF_CLOSING;
73752419Sjulian
738129836Sjulian	/* If node has its pre-shutdown method, then call it first*/
739129836Sjulian	if (node->nd_type && node->nd_type->close)
740129836Sjulian		(*node->nd_type->close)(node);
741129836Sjulian
74270939Sjulian	/* Notify all remaining connected nodes to disconnect */
74370939Sjulian	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
74470939Sjulian		ng_destroy_hook(hook);
74570784Sjulian
74670700Sjulian	/*
74770700Sjulian	 * Drain the input queue forceably.
74870784Sjulian	 * it has no hooks so what's it going to do, bleed on someone?
74970784Sjulian	 * Theoretically we came here from a queue entry that was added
75070784Sjulian	 * Just before the queue was closed, so it should be empty anyway.
75171902Sjulian	 * Also removes us from worklist if needed.
75270700Sjulian	 */
753178228Smav	ng_flush_input_queue(node);
75470700Sjulian
75552419Sjulian	/* Ask the type if it has anything to do in this case */
75670784Sjulian	if (node->nd_type && node->nd_type->shutdown) {
75770784Sjulian		(*node->nd_type->shutdown)(node);
75871849Sjulian		if (NG_NODE_IS_VALID(node)) {
75971849Sjulian			/*
76071849Sjulian			 * Well, blow me down if the node code hasn't declared
76171849Sjulian			 * that it doesn't want to die.
762298813Spfg			 * Presumably it is a persistent node.
76371849Sjulian			 * If we REALLY want it to go away,
76471849Sjulian			 *  e.g. hardware going away,
765132464Sjulian			 * Our caller should set NGF_REALLY_DIE in nd_flags.
766152451Sglebius			 */
767132464Sjulian			node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
76871849Sjulian			NG_NODE_UNREF(node); /* Assume they still have theirs */
76971849Sjulian			return;
77071849Sjulian		}
77170700Sjulian	} else {				/* do the default thing */
77270784Sjulian		NG_NODE_UNREF(node);
77352419Sjulian	}
77452419Sjulian
77570784Sjulian	ng_unname(node); /* basically a NOP these days */
77670784Sjulian
77770784Sjulian	/*
77870784Sjulian	 * Remove extra reference, possibly the last
77970784Sjulian	 * Possible other holders of references may include
78070784Sjulian	 * timeout callouts, but theoretically the node's supposed to
78170784Sjulian	 * have cancelled them. Possibly hardware dependencies may
78270784Sjulian	 * force a driver to 'linger' with a reference.
78370784Sjulian	 */
78470784Sjulian	NG_NODE_UNREF(node);
78552419Sjulian}
78652419Sjulian
78752419Sjulian/*
78874078Sjulian * Remove a reference to the node, possibly the last.
78974078Sjulian * deadnode always acts as it it were the last.
79052419Sjulian */
791223754Sglebiusvoid
79270784Sjulianng_unref_node(node_p node)
79352419Sjulian{
79471047Sjulian
795223754Sglebius	if (node == &ng_deadnode)
796223754Sglebius		return;
79771047Sjulian
798253346Srodrigc	CURVNET_SET(node->nd_vnet);
799253346Srodrigc
800223754Sglebius	if (refcount_release(&node->nd_refs)) { /* we were the last */
80169519Sjulian
80270784Sjulian		node->nd_type->refs--; /* XXX maybe should get types lock? */
803230480Sglebius		NAMEHASH_WLOCK();
804231831Sglebius		if (NG_NODE_HAS_NAME(node)) {
805231831Sglebius			V_ng_named_nodes--;
806231831Sglebius			LIST_REMOVE(node, nd_nodes);
807231831Sglebius		}
808230480Sglebius		NAMEHASH_WUNLOCK();
80970700Sjulian
810230480Sglebius		IDHASH_WLOCK();
811231831Sglebius		V_ng_nodes--;
81270784Sjulian		LIST_REMOVE(node, nd_idnodes);
813230480Sglebius		IDHASH_WUNLOCK();
81452419Sjulian
81570791Sjulian		mtx_destroy(&node->nd_input_queue.q_mtx);
81670700Sjulian		NG_FREE_NODE(node);
81752419Sjulian	}
818253346Srodrigc	CURVNET_RESTORE();
81952419Sjulian}
82052419Sjulian
82152419Sjulian/************************************************************************
82252722Sjulian			Node ID handling
82352722Sjulian************************************************************************/
82452722Sjulianstatic node_p
82570700Sjulianng_ID2noderef(ng_ID_t ID)
82652722Sjulian{
82770784Sjulian	node_p node;
828231831Sglebius
829230480Sglebius	IDHASH_RLOCK();
83071354Sjulian	NG_IDHASH_FIND(ID, node);
831231831Sglebius	if (node)
83270784Sjulian		NG_NODE_REF(node);
833230480Sglebius	IDHASH_RUNLOCK();
83470784Sjulian	return(node);
83552722Sjulian}
83652722Sjulian
83752722Sjulianng_ID_t
83852722Sjulianng_node2ID(node_p node)
83952722Sjulian{
84070912Sjulian	return (node ? NG_NODE_ID(node) : 0);
84152722Sjulian}
84252722Sjulian
84352722Sjulian/************************************************************************
84452419Sjulian			Node name handling
84552419Sjulian************************************************************************/
84652419Sjulian
84752419Sjulian/*
848229003Sglebius * Assign a node a name.
84952419Sjulian */
85052419Sjulianint
85152419Sjulianng_name_node(node_p node, const char *name)
85252419Sjulian{
853231831Sglebius	uint32_t hash;
85470700Sjulian	node_p node2;
855231831Sglebius	int i;
85652419Sjulian
857369960Sgit2svn	/* Rename without change is a noop */
858369960Sgit2svn	if (strcmp(NG_NODE_NAME(node), name) == 0)
859369960Sgit2svn		return (0);
860369960Sgit2svn
86152419Sjulian	/* Check the name is valid */
862125028Sharti	for (i = 0; i < NG_NODESIZ; i++) {
86352419Sjulian		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
86452419Sjulian			break;
86552419Sjulian	}
86652419Sjulian	if (i == 0 || name[i] != '\0') {
86771047Sjulian		TRAP_ERROR();
86852419Sjulian		return (EINVAL);
86952419Sjulian	}
87052722Sjulian	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
87171047Sjulian		TRAP_ERROR();
87252419Sjulian		return (EINVAL);
87352419Sjulian	}
87452419Sjulian
875231831Sglebius	NAMEHASH_WLOCK();
876231831Sglebius	if (V_ng_named_nodes * 2 > V_ng_name_hmask)
877231831Sglebius		ng_name_rehash();
87852419Sjulian
879231831Sglebius	hash = hash32_str(name, HASHINIT) & V_ng_name_hmask;
880231831Sglebius	/* Check the name isn't already being used. */
881231831Sglebius	LIST_FOREACH(node2, &V_ng_name_hash[hash], nd_nodes)
882231831Sglebius		if (NG_NODE_IS_VALID(node2) &&
883231831Sglebius		    (strcmp(NG_NODE_NAME(node2), name) == 0)) {
884231831Sglebius			NAMEHASH_WUNLOCK();
885231831Sglebius			return (EADDRINUSE);
886231831Sglebius		}
887231831Sglebius
888231831Sglebius	if (NG_NODE_HAS_NAME(node))
889231831Sglebius		LIST_REMOVE(node, nd_nodes);
890231831Sglebius	else
891231831Sglebius		V_ng_named_nodes++;
892231831Sglebius	/* Copy it. */
893125028Sharti	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
894176802Smav	/* Update name hash. */
895181803Sbz	LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes);
896230480Sglebius	NAMEHASH_WUNLOCK();
897176802Smav
89852419Sjulian	return (0);
89952419Sjulian}
90052419Sjulian
90152419Sjulian/*
90252419Sjulian * Find a node by absolute name. The name should NOT end with ':'
90352419Sjulian * The name "." means "this node" and "[xxx]" means "the node
90452419Sjulian * with ID (ie, at address) xxx".
90552419Sjulian *
90652419Sjulian * Returns the node if found, else NULL.
90770700Sjulian * Eventually should add something faster than a sequential search.
908170035Srwatson * Note it acquires a reference on the node so you can be sure it's still
909170035Srwatson * there.
91052419Sjulian */
91152419Sjuliannode_p
91270700Sjulianng_name2noderef(node_p here, const char *name)
91352419Sjulian{
91452722Sjulian	node_p node;
91552722Sjulian	ng_ID_t temp;
916176802Smav	int	hash;
91752419Sjulian
91852419Sjulian	/* "." means "this node" */
91970700Sjulian	if (strcmp(name, ".") == 0) {
92070784Sjulian		NG_NODE_REF(here);
92170700Sjulian		return(here);
92270700Sjulian	}
92352419Sjulian
92452419Sjulian	/* Check for name-by-ID */
92552722Sjulian	if ((temp = ng_decodeidname(name)) != 0) {
92670700Sjulian		return (ng_ID2noderef(temp));
92752419Sjulian	}
92852419Sjulian
929231831Sglebius	/* Find node by name. */
930231831Sglebius	hash = hash32_str(name, HASHINIT) & V_ng_name_hmask;
931230480Sglebius	NAMEHASH_RLOCK();
932230480Sglebius	LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes)
933176802Smav		if (NG_NODE_IS_VALID(node) &&
934176802Smav		    (strcmp(NG_NODE_NAME(node), name) == 0)) {
935230480Sglebius			NG_NODE_REF(node);
93652419Sjulian			break;
93770912Sjulian		}
938230480Sglebius	NAMEHASH_RUNLOCK();
939230480Sglebius
94052419Sjulian	return (node);
94152419Sjulian}
94252419Sjulian
94352419Sjulian/*
944108533Sschweikh * Decode an ID name, eg. "[f03034de]". Returns 0 if the
94552722Sjulian * string is not valid, otherwise returns the value.
94652419Sjulian */
94752722Sjulianstatic ng_ID_t
94852419Sjulianng_decodeidname(const char *name)
94952419Sjulian{
95052816Sarchie	const int len = strlen(name);
95153648Sarchie	char *eptr;
95252816Sarchie	u_long val;
95352419Sjulian
95452816Sarchie	/* Check for proper length, brackets, no leading junk */
955229003Sglebius	if ((len < 3) || (name[0] != '[') || (name[len - 1] != ']') ||
956229003Sglebius	    (!isxdigit(name[1])))
95770912Sjulian		return ((ng_ID_t)0);
95852419Sjulian
95952816Sarchie	/* Decode number */
96052816Sarchie	val = strtoul(name + 1, &eptr, 16);
961229003Sglebius	if ((eptr - name != len - 1) || (val == ULONG_MAX) || (val == 0))
96253042Sjulian		return ((ng_ID_t)0);
963229003Sglebius
964229003Sglebius	return ((ng_ID_t)val);
96552419Sjulian}
96652419Sjulian
96752419Sjulian/*
96852419Sjulian * Remove a name from a node. This should only be called
96952419Sjulian * when shutting down and removing the node.
97052419Sjulian */
97152419Sjulianvoid
97252419Sjulianng_unname(node_p node)
97352419Sjulian{
97452419Sjulian}
97552419Sjulian
976231831Sglebius/*
977231831Sglebius * Allocate a bigger name hash.
978231831Sglebius */
979231831Sglebiusstatic void
980231831Sglebiusng_name_rehash()
981231831Sglebius{
982231831Sglebius	struct nodehash *new;
983231831Sglebius	uint32_t hash;
984231831Sglebius	u_long hmask;
985231831Sglebius	node_p node, node2;
986231831Sglebius	int i;
987231831Sglebius
988231831Sglebius	new = hashinit_flags((V_ng_name_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask,
989231831Sglebius	    HASH_NOWAIT);
990231831Sglebius	if (new == NULL)
991231831Sglebius		return;
992231831Sglebius
993231831Sglebius	for (i = 0; i <= V_ng_name_hmask; i++)
994231831Sglebius		LIST_FOREACH_SAFE(node, &V_ng_name_hash[i], nd_nodes, node2) {
995231831Sglebius#ifdef INVARIANTS
996231831Sglebius			LIST_REMOVE(node, nd_nodes);
997231831Sglebius#endif
998231831Sglebius			hash = hash32_str(NG_NODE_NAME(node), HASHINIT) & hmask;
999231831Sglebius			LIST_INSERT_HEAD(&new[hash], node, nd_nodes);
1000231831Sglebius		}
1001231831Sglebius
1002231831Sglebius	hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
1003231831Sglebius	V_ng_name_hash = new;
1004231831Sglebius	V_ng_name_hmask = hmask;
1005231831Sglebius}
1006231831Sglebius
1007231831Sglebius/*
1008231831Sglebius * Allocate a bigger ID hash.
1009231831Sglebius */
1010231831Sglebiusstatic void
1011231831Sglebiusng_ID_rehash()
1012231831Sglebius{
1013231831Sglebius	struct nodehash *new;
1014231831Sglebius	uint32_t hash;
1015231831Sglebius	u_long hmask;
1016231831Sglebius	node_p node, node2;
1017231831Sglebius	int i;
1018231831Sglebius
1019231831Sglebius	new = hashinit_flags((V_ng_ID_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask,
1020231831Sglebius	    HASH_NOWAIT);
1021231831Sglebius	if (new == NULL)
1022231831Sglebius		return;
1023231831Sglebius
1024231831Sglebius	for (i = 0; i <= V_ng_ID_hmask; i++)
1025231831Sglebius		LIST_FOREACH_SAFE(node, &V_ng_ID_hash[i], nd_idnodes, node2) {
1026231831Sglebius#ifdef INVARIANTS
1027231831Sglebius			LIST_REMOVE(node, nd_idnodes);
1028231831Sglebius#endif
1029231831Sglebius			hash = (node->nd_ID % (hmask + 1));
1030231831Sglebius			LIST_INSERT_HEAD(&new[hash], node, nd_idnodes);
1031231831Sglebius		}
1032231831Sglebius
1033231831Sglebius	hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
1034231831Sglebius	V_ng_ID_hash = new;
1035231831Sglebius	V_ng_ID_hmask = hmask;
1036231831Sglebius}
1037231831Sglebius
103852419Sjulian/************************************************************************
103952419Sjulian			Hook routines
104052419Sjulian Names are not optional. Hooks are always connected, except for a
104170939Sjulian brief moment within these routines. On invalidation or during creation
104270939Sjulian they are connected to the 'dead' hook.
104352419Sjulian************************************************************************/
104452419Sjulian
104552419Sjulian/*
104652419Sjulian * Remove a hook reference
104752419Sjulian */
104870784Sjulianvoid
104952419Sjulianng_unref_hook(hook_p hook)
105052419Sjulian{
105171047Sjulian
1052223754Sglebius	if (hook == &ng_deadhook)
105371047Sjulian		return;
105469519Sjulian
1055223754Sglebius	if (refcount_release(&hook->hk_refs)) { /* we were the last */
1056177722Smav		if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */
105771047Sjulian			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
105870700Sjulian		NG_FREE_HOOK(hook);
105970700Sjulian	}
106052419Sjulian}
106152419Sjulian
106252419Sjulian/*
106352419Sjulian * Add an unconnected hook to a node. Only used internally.
106470939Sjulian * Assumes node is locked. (XXX not yet true )
106552419Sjulian */
106652419Sjulianstatic int
106752419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp)
106852419Sjulian{
106952419Sjulian	hook_p hook;
107052419Sjulian	int error = 0;
107152419Sjulian
107252419Sjulian	/* Check that the given name is good */
107352419Sjulian	if (name == NULL) {
107471047Sjulian		TRAP_ERROR();
107552419Sjulian		return (EINVAL);
107652419Sjulian	}
107754096Sarchie	if (ng_findhook(node, name) != NULL) {
107871047Sjulian		TRAP_ERROR();
107954096Sarchie		return (EEXIST);
108052419Sjulian	}
108152419Sjulian
108252419Sjulian	/* Allocate the hook and link it up */
108370784Sjulian	NG_ALLOC_HOOK(hook);
108452419Sjulian	if (hook == NULL) {
108571047Sjulian		TRAP_ERROR();
108652419Sjulian		return (ENOMEM);
108752419Sjulian	}
108870939Sjulian	hook->hk_refs = 1;		/* add a reference for us to return */
108970784Sjulian	hook->hk_flags = HK_INVALID;
109070939Sjulian	hook->hk_peer = &ng_deadhook;	/* start off this way */
109170784Sjulian	hook->hk_node = node;
109270784Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
109352419Sjulian
109470939Sjulian	/* Set hook name */
1095125028Sharti	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
109670939Sjulian
109770939Sjulian	/*
109870939Sjulian	 * Check if the node type code has something to say about it
109970939Sjulian	 * If it fails, the unref of the hook will also unref the node.
110070939Sjulian	 */
110170935Sjulian	if (node->nd_type->newhook != NULL) {
110270935Sjulian		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
110370784Sjulian			NG_HOOK_UNREF(hook);	/* this frees the hook */
110470700Sjulian			return (error);
110570700Sjulian		}
110670935Sjulian	}
110752419Sjulian	/*
110852419Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
110952419Sjulian	 * We'll ask again later when we actually connect the hooks.
111052419Sjulian	 */
111170784Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
111270784Sjulian	node->nd_numhooks++;
111370939Sjulian	NG_HOOK_REF(hook);	/* one for the node */
111452419Sjulian
111552419Sjulian	if (hookp)
111652419Sjulian		*hookp = hook;
111770939Sjulian	return (0);
111852419Sjulian}
111952419Sjulian
112052419Sjulian/*
112154096Sarchie * Find a hook
112254096Sarchie *
112354096Sarchie * Node types may supply their own optimized routines for finding
112454096Sarchie * hooks.  If none is supplied, we just do a linear search.
112570939Sjulian * XXX Possibly we should add a reference to the hook?
112654096Sarchie */
112754096Sarchiehook_p
112854096Sarchieng_findhook(node_p node, const char *name)
112954096Sarchie{
113054096Sarchie	hook_p hook;
113154096Sarchie
113270784Sjulian	if (node->nd_type->findhook != NULL)
113370784Sjulian		return (*node->nd_type->findhook)(node, name);
113470784Sjulian	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
1135229003Sglebius		if (NG_HOOK_IS_VALID(hook) &&
1136229003Sglebius		    (strcmp(NG_HOOK_NAME(hook), name) == 0))
113754096Sarchie			return (hook);
113854096Sarchie	}
113954096Sarchie	return (NULL);
114054096Sarchie}
114154096Sarchie
114254096Sarchie/*
114352419Sjulian * Destroy a hook
114452419Sjulian *
114552419Sjulian * As hooks are always attached, this really destroys two hooks.
114652419Sjulian * The one given, and the one attached to it. Disconnect the hooks
114770939Sjulian * from each other first. We reconnect the peer hook to the 'dead'
114870939Sjulian * hook so that it can still exist after we depart. We then
114970939Sjulian * send the peer its own destroy message. This ensures that we only
1150152451Sglebius * interact with the peer's structures when it is locked processing that
115170939Sjulian * message. We hold a reference to the peer hook so we are guaranteed that
115270939Sjulian * the peer hook and node are still going to exist until
115370939Sjulian * we are finished there as the hook holds a ref on the node.
1154152451Sglebius * We run this same code again on the peer hook, but that time it is already
1155152451Sglebius * attached to the 'dead' hook.
115671047Sjulian *
1157152451Sglebius * This routine is called at all stages of hook creation
115871047Sjulian * on error detection and must be able to handle any such stage.
115952419Sjulian */
116052419Sjulianvoid
116152419Sjulianng_destroy_hook(hook_p hook)
116252419Sjulian{
1163151974Sglebius	hook_p peer;
1164151974Sglebius	node_p node;
116552419Sjulian
116671047Sjulian	if (hook == &ng_deadhook) {	/* better safe than sorry */
116771047Sjulian		printf("ng_destroy_hook called on deadhook\n");
116871047Sjulian		return;
116971047Sjulian	}
1170151974Sglebius
1171151974Sglebius	/*
1172151974Sglebius	 * Protect divorce process with mutex, to avoid races on
1173151974Sglebius	 * simultaneous disconnect.
1174151974Sglebius	 */
1175256550Smelifaro	TOPOLOGY_WLOCK();
1176151974Sglebius
1177151974Sglebius	hook->hk_flags |= HK_INVALID;
1178151974Sglebius
1179151974Sglebius	peer = NG_HOOK_PEER(hook);
1180151974Sglebius	node = NG_HOOK_NODE(hook);
1181151974Sglebius
118270939Sjulian	if (peer && (peer != &ng_deadhook)) {
118370939Sjulian		/*
118470939Sjulian		 * Set the peer to point to ng_deadhook
118570939Sjulian		 * from this moment on we are effectively independent it.
118670939Sjulian		 * send it an rmhook message of it's own.
118770939Sjulian		 */
118870939Sjulian		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
118970939Sjulian		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
119071047Sjulian		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
1191152451Sglebius			/*
119271047Sjulian			 * If it's already divorced from a node,
119371047Sjulian			 * just free it.
119471047Sjulian			 */
1195256550Smelifaro			TOPOLOGY_WUNLOCK();
119671047Sjulian		} else {
1197256550Smelifaro			TOPOLOGY_WUNLOCK();
119871047Sjulian			ng_rmhook_self(peer); 	/* Send it a surprise */
119971047Sjulian		}
120070942Sjulian		NG_HOOK_UNREF(peer);		/* account for peer link */
120170942Sjulian		NG_HOOK_UNREF(hook);		/* account for peer link */
1202151974Sglebius	} else
1203256550Smelifaro		TOPOLOGY_WUNLOCK();
120452419Sjulian
1205256550Smelifaro	TOPOLOGY_NOTOWNED();
1206151974Sglebius
120752419Sjulian	/*
120852419Sjulian	 * Remove the hook from the node's list to avoid possible recursion
120952419Sjulian	 * in case the disconnection results in node shutdown.
121052419Sjulian	 */
121171047Sjulian	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
121271047Sjulian		return;
121371047Sjulian	}
121470784Sjulian	LIST_REMOVE(hook, hk_hooks);
121570784Sjulian	node->nd_numhooks--;
121670784Sjulian	if (node->nd_type->disconnect) {
121752419Sjulian		/*
121871047Sjulian		 * The type handler may elect to destroy the node so don't
1219167677Srwatson		 * trust its existence after this point. (except
122071047Sjulian		 * that we still hold a reference on it. (which we
122171047Sjulian		 * inherrited from the hook we are destroying)
122252419Sjulian		 */
122370784Sjulian		(*node->nd_type->disconnect) (hook);
122452419Sjulian	}
122571047Sjulian
122671047Sjulian	/*
122771047Sjulian	 * Note that because we will point to ng_deadnode, the original node
122871047Sjulian	 * is not decremented automatically so we do that manually.
122971047Sjulian	 */
123071047Sjulian	_NG_HOOK_NODE(hook) = &ng_deadnode;
123171047Sjulian	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
123271047Sjulian	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
123352419Sjulian}
123452419Sjulian
123552419Sjulian/*
123652419Sjulian * Take two hooks on a node and merge the connection so that the given node
123752419Sjulian * is effectively bypassed.
123852419Sjulian */
123952419Sjulianint
124052419Sjulianng_bypass(hook_p hook1, hook_p hook2)
124152419Sjulian{
124270784Sjulian	if (hook1->hk_node != hook2->hk_node) {
124371047Sjulian		TRAP_ERROR();
124452419Sjulian		return (EINVAL);
124570784Sjulian	}
1246256550Smelifaro	TOPOLOGY_WLOCK();
1247231761Sglebius	if (NG_HOOK_NOT_VALID(hook1) || NG_HOOK_NOT_VALID(hook2)) {
1248256550Smelifaro		TOPOLOGY_WUNLOCK();
1249231761Sglebius		return (EINVAL);
1250231761Sglebius	}
125170784Sjulian	hook1->hk_peer->hk_peer = hook2->hk_peer;
125270784Sjulian	hook2->hk_peer->hk_peer = hook1->hk_peer;
125352419Sjulian
125470939Sjulian	hook1->hk_peer = &ng_deadhook;
125570939Sjulian	hook2->hk_peer = &ng_deadhook;
1256256550Smelifaro	TOPOLOGY_WUNLOCK();
125770939Sjulian
1258163244Sglebius	NG_HOOK_UNREF(hook1);
1259163244Sglebius	NG_HOOK_UNREF(hook2);
1260163244Sglebius
126152419Sjulian	/* XXX If we ever cache methods on hooks update them as well */
126252419Sjulian	ng_destroy_hook(hook1);
126352419Sjulian	ng_destroy_hook(hook2);
126452419Sjulian	return (0);
126552419Sjulian}
126652419Sjulian
126752419Sjulian/*
126852419Sjulian * Install a new netgraph type
126952419Sjulian */
127052419Sjulianint
127152419Sjulianng_newtype(struct ng_type *tp)
127252419Sjulian{
127352419Sjulian	const size_t namelen = strlen(tp->name);
127452419Sjulian
127552419Sjulian	/* Check version and type name fields */
1276229003Sglebius	if ((tp->version != NG_ABI_VERSION) || (namelen == 0) ||
1277229003Sglebius	    (namelen >= NG_TYPESIZ)) {
127871047Sjulian		TRAP_ERROR();
1279131374Sjulian		if (tp->version != NG_ABI_VERSION) {
1280229003Sglebius			printf("Netgraph: Node type rejected. ABI mismatch. "
1281229003Sglebius			    "Suggest recompile\n");
1282131374Sjulian		}
128352419Sjulian		return (EINVAL);
128452419Sjulian	}
128552419Sjulian
128652419Sjulian	/* Check for name collision */
128752419Sjulian	if (ng_findtype(tp->name) != NULL) {
128871047Sjulian		TRAP_ERROR();
128952419Sjulian		return (EEXIST);
129052419Sjulian	}
129152419Sjulian
129252419Sjulian	/* Link in new type */
1293230480Sglebius	TYPELIST_WLOCK();
129470700Sjulian	LIST_INSERT_HEAD(&ng_typelist, tp, types);
129571603Sjulian	tp->refs = 1;	/* first ref is linked list */
1296230480Sglebius	TYPELIST_WUNLOCK();
129752419Sjulian	return (0);
129852419Sjulian}
129952419Sjulian
130052419Sjulian/*
130180222Sjulian * unlink a netgraph type
130280222Sjulian * If no examples exist
130380222Sjulian */
130480222Sjulianint
130580222Sjulianng_rmtype(struct ng_type *tp)
130680222Sjulian{
130780222Sjulian	/* Check for name collision */
130880222Sjulian	if (tp->refs != 1) {
130980222Sjulian		TRAP_ERROR();
131080222Sjulian		return (EBUSY);
131180222Sjulian	}
131280222Sjulian
131380222Sjulian	/* Unlink type */
1314230480Sglebius	TYPELIST_WLOCK();
131580222Sjulian	LIST_REMOVE(tp, types);
1316230480Sglebius	TYPELIST_WUNLOCK();
131780222Sjulian	return (0);
131880222Sjulian}
131980222Sjulian
132080222Sjulian/*
132152419Sjulian * Look for a type of the name given
132252419Sjulian */
132352419Sjulianstruct ng_type *
132452419Sjulianng_findtype(const char *typename)
132552419Sjulian{
132652419Sjulian	struct ng_type *type;
132752419Sjulian
1328230480Sglebius	TYPELIST_RLOCK();
132970700Sjulian	LIST_FOREACH(type, &ng_typelist, types) {
133052419Sjulian		if (strcmp(type->name, typename) == 0)
133152419Sjulian			break;
133252419Sjulian	}
1333230480Sglebius	TYPELIST_RUNLOCK();
133452419Sjulian	return (type);
133552419Sjulian}
133652419Sjulian
133752419Sjulian/************************************************************************
133852419Sjulian			Composite routines
133952419Sjulian************************************************************************/
134052419Sjulian/*
134171047Sjulian * Connect two nodes using the specified hooks, using queued functions.
134252419Sjulian */
1343172806Smavstatic int
1344172806Smavng_con_part3(node_p node, item_p item, hook_p hook)
134552419Sjulian{
1346172806Smav	int	error = 0;
134752419Sjulian
134871047Sjulian	/*
134971047Sjulian	 * When we run, we know that the node 'node' is locked for us.
135071047Sjulian	 * Our caller has a reference on the hook.
135171047Sjulian	 * Our caller has a reference on the node.
135271047Sjulian	 * (In this case our caller is ng_apply_item() ).
135371047Sjulian	 * The peer hook has a reference on the hook.
135471849Sjulian	 * We are all set up except for the final call to the node, and
135571849Sjulian	 * the clearing of the INVALID flag.
135671047Sjulian	 */
135771047Sjulian	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
135871047Sjulian		/*
135971047Sjulian		 * The node must have been freed again since we last visited
136071047Sjulian		 * here. ng_destry_hook() has this effect but nothing else does.
136171047Sjulian		 * We should just release our references and
136271047Sjulian		 * free anything we can think of.
136371047Sjulian		 * Since we know it's been destroyed, and it's our caller
136471047Sjulian		 * that holds the references, just return.
136571047Sjulian		 */
1366172806Smav		ERROUT(ENOENT);
136752419Sjulian	}
136871047Sjulian	if (hook->hk_node->nd_type->connect) {
1369172806Smav		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
137071047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
137171849Sjulian			printf("failed in ng_con_part3()\n");
1372172806Smav			ERROUT(error);
137371047Sjulian		}
137452419Sjulian	}
137571047Sjulian	/*
137671047Sjulian	 *  XXX this is wrong for SMP. Possibly we need
137771047Sjulian	 * to separate out 'create' and 'invalid' flags.
137871047Sjulian	 * should only set flags on hooks we have locked under our node.
137971047Sjulian	 */
138071047Sjulian	hook->hk_flags &= ~HK_INVALID;
1381172806Smavdone:
1382172806Smav	NG_FREE_ITEM(item);
1383172806Smav	return (error);
138471047Sjulian}
138552419Sjulian
1386172806Smavstatic int
1387172806Smavng_con_part2(node_p node, item_p item, hook_p hook)
138871047Sjulian{
1389172806Smav	hook_p	peer;
1390172806Smav	int	error = 0;
139171047Sjulian
139252419Sjulian	/*
139371047Sjulian	 * When we run, we know that the node 'node' is locked for us.
139471047Sjulian	 * Our caller has a reference on the hook.
139571047Sjulian	 * Our caller has a reference on the node.
139671047Sjulian	 * (In this case our caller is ng_apply_item() ).
139771047Sjulian	 * The peer hook has a reference on the hook.
139871047Sjulian	 * our node pointer points to the 'dead' node.
139971047Sjulian	 * First check the hook name is unique.
140071849Sjulian	 * Should not happen because we checked before queueing this.
140152419Sjulian	 */
140271047Sjulian	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
140371047Sjulian		TRAP_ERROR();
140471047Sjulian		ng_destroy_hook(hook); /* should destroy peer too */
140571849Sjulian		printf("failed in ng_con_part2()\n");
1406172806Smav		ERROUT(EEXIST);
140771047Sjulian	}
140870939Sjulian	/*
140971047Sjulian	 * Check if the node type code has something to say about it
141071047Sjulian	 * If it fails, the unref of the hook will also unref the attached node,
141171047Sjulian	 * however since that node is 'ng_deadnode' this will do nothing.
141271047Sjulian	 * The peer hook will also be destroyed.
141370939Sjulian	 */
141471047Sjulian	if (node->nd_type->newhook != NULL) {
1415172806Smav		if ((error = (*node->nd_type->newhook)(node, hook,
1416172806Smav		    hook->hk_name))) {
141771047Sjulian			ng_destroy_hook(hook); /* should destroy peer too */
141871849Sjulian			printf("failed in ng_con_part2()\n");
1419172806Smav			ERROUT(error);
142071047Sjulian		}
142171047Sjulian	}
142271047Sjulian
142371047Sjulian	/*
142471047Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
142571047Sjulian	 * We'll ask again later when we actually connect the hooks.
142671047Sjulian	 */
142771047Sjulian	hook->hk_node = node;		/* just overwrite ng_deadnode */
142871047Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
142971047Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
143071047Sjulian	node->nd_numhooks++;
143171047Sjulian	NG_HOOK_REF(hook);	/* one for the node */
143271047Sjulian
143371047Sjulian	/*
1434167677Srwatson	 * We now have a symmetrical situation, where both hooks have been
143574078Sjulian	 * linked to their nodes, the newhook methods have been called
143671047Sjulian	 * And the references are all correct. The hooks are still marked
143771047Sjulian	 * as invalid, as we have not called the 'connect' methods
143871047Sjulian	 * yet.
1439167677Srwatson	 * We can call the local one immediately as we have the
144071047Sjulian	 * node locked, but we need to queue the remote one.
144171047Sjulian	 */
144271047Sjulian	if (hook->hk_node->nd_type->connect) {
1443172806Smav		if ((error = (*hook->hk_node->nd_type->connect) (hook))) {
144471047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
144571849Sjulian			printf("failed in ng_con_part2(A)\n");
1446172806Smav			ERROUT(error);
144771047Sjulian		}
144871047Sjulian	}
1449151974Sglebius
1450151974Sglebius	/*
1451151974Sglebius	 * Acquire topo mutex to avoid race with ng_destroy_hook().
1452151974Sglebius	 */
1453256550Smelifaro	TOPOLOGY_RLOCK();
1454151974Sglebius	peer = hook->hk_peer;
1455151974Sglebius	if (peer == &ng_deadhook) {
1456256550Smelifaro		TOPOLOGY_RUNLOCK();
1457151974Sglebius		printf("failed in ng_con_part2(B)\n");
1458151974Sglebius		ng_destroy_hook(hook);
1459172806Smav		ERROUT(ENOENT);
1460151974Sglebius	}
1461256550Smelifaro	TOPOLOGY_RUNLOCK();
1462151974Sglebius
1463173605Sglebius	if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3,
1464173605Sglebius	    NULL, 0, NG_REUSE_ITEM))) {
1465151974Sglebius		printf("failed in ng_con_part2(C)\n");
146671849Sjulian		ng_destroy_hook(hook);	/* also zaps peer */
1467172806Smav		return (error);		/* item was consumed. */
146871849Sjulian	}
146971047Sjulian	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
1470172806Smav	return (0);			/* item was consumed. */
1471172806Smavdone:
1472172806Smav	NG_FREE_ITEM(item);
1473172806Smav	return (error);
147452419Sjulian}
147552419Sjulian
147652419Sjulian/*
1477152451Sglebius * Connect this node with another node. We assume that this node is
147871047Sjulian * currently locked, as we are only called from an NGM_CONNECT message.
147952419Sjulian */
148071047Sjulianstatic int
1481172806Smavng_con_nodes(item_p item, node_p node, const char *name,
1482172806Smav    node_p node2, const char *name2)
148352419Sjulian{
1484152451Sglebius	int	error;
1485152451Sglebius	hook_p	hook;
1486152451Sglebius	hook_p	hook2;
148752419Sjulian
148871849Sjulian	if (ng_findhook(node2, name2) != NULL) {
148971849Sjulian		return(EEXIST);
149071849Sjulian	}
149170939Sjulian	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
149252419Sjulian		return (error);
149371047Sjulian	/* Allocate the other hook and link it up */
149471047Sjulian	NG_ALLOC_HOOK(hook2);
1495140737Sglebius	if (hook2 == NULL) {
149671047Sjulian		TRAP_ERROR();
149771047Sjulian		ng_destroy_hook(hook);	/* XXX check ref counts so far */
149871047Sjulian		NG_HOOK_UNREF(hook);	/* including our ref */
149971047Sjulian		return (ENOMEM);
150071047Sjulian	}
150171047Sjulian	hook2->hk_refs = 1;		/* start with a reference for us. */
150271047Sjulian	hook2->hk_flags = HK_INVALID;
150371047Sjulian	hook2->hk_peer = hook;		/* Link the two together */
150471047Sjulian	hook->hk_peer = hook2;
150571047Sjulian	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
150671047Sjulian	NG_HOOK_REF(hook2);
1507152451Sglebius	hook2->hk_node = &ng_deadnode;
1508125028Sharti	strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
150971047Sjulian
151071047Sjulian	/*
151171047Sjulian	 * Queue the function above.
151271047Sjulian	 * Procesing continues in that function in the lock context of
151371047Sjulian	 * the other node.
151471047Sjulian	 */
1515173605Sglebius	if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0,
1516173605Sglebius	    NG_NOFLAGS))) {
1517171885Smav		printf("failed in ng_con_nodes(): %d\n", error);
1518171885Smav		ng_destroy_hook(hook);	/* also zaps peer */
1519171885Smav	}
152071047Sjulian
152171047Sjulian	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
152271047Sjulian	NG_HOOK_UNREF(hook2);
1523171885Smav	return (error);
152471047Sjulian}
152571047Sjulian
152671047Sjulian/*
152771047Sjulian * Make a peer and connect.
152871047Sjulian * We assume that the local node is locked.
152971047Sjulian * The new node probably doesn't need a lock until
153071047Sjulian * it has a hook, because it cannot really have any work until then,
153171047Sjulian * but we should think about it a bit more.
153271047Sjulian *
153371047Sjulian * The problem may come if the other node also fires up
153471047Sjulian * some hardware or a timer or some other source of activation,
153571047Sjulian * also it may already get a command msg via it's ID.
153671047Sjulian *
153771047Sjulian * We could use the same method as ng_con_nodes() but we'd have
1538152451Sglebius * to add ability to remove the node when failing. (Not hard, just
153971047Sjulian * make arg1 point to the node to remove).
154071047Sjulian * Unless of course we just ignore failure to connect and leave
154171047Sjulian * an unconnected node?
154271047Sjulian */
154371047Sjulianstatic int
154471047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type)
154571047Sjulian{
1546152451Sglebius	node_p	node2;
1547152451Sglebius	hook_p	hook1, hook2;
1548152451Sglebius	int	error;
154971047Sjulian
155071047Sjulian	if ((error = ng_make_node(type, &node2))) {
155152419Sjulian		return (error);
155252419Sjulian	}
155370939Sjulian
155471047Sjulian	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
155571849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
155671047Sjulian		return (error);
155771047Sjulian	}
155871047Sjulian
155971047Sjulian	if ((error = ng_add_hook(node2, name2, &hook2))) {
156071849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
156171047Sjulian		ng_destroy_hook(hook1);
156271047Sjulian		NG_HOOK_UNREF(hook1);
156371047Sjulian		return (error);
156471047Sjulian	}
156571047Sjulian
156670939Sjulian	/*
156771047Sjulian	 * Actually link the two hooks together.
156871047Sjulian	 */
156971047Sjulian	hook1->hk_peer = hook2;
157071047Sjulian	hook2->hk_peer = hook1;
157171047Sjulian
157271047Sjulian	/* Each hook is referenced by the other */
157371047Sjulian	NG_HOOK_REF(hook1);
157471047Sjulian	NG_HOOK_REF(hook2);
157571047Sjulian
157671047Sjulian	/* Give each node the opportunity to veto the pending connection */
157771047Sjulian	if (hook1->hk_node->nd_type->connect) {
157871047Sjulian		error = (*hook1->hk_node->nd_type->connect) (hook1);
157971047Sjulian	}
158071047Sjulian
158171047Sjulian	if ((error == 0) && hook2->hk_node->nd_type->connect) {
158271047Sjulian		error = (*hook2->hk_node->nd_type->connect) (hook2);
158371047Sjulian
158471047Sjulian	}
158571047Sjulian
158671047Sjulian	/*
158770939Sjulian	 * drop the references we were holding on the two hooks.
158870939Sjulian	 */
158971047Sjulian	if (error) {
159071047Sjulian		ng_destroy_hook(hook2);	/* also zaps hook1 */
159171849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
159271047Sjulian	} else {
159371047Sjulian		/* As a last act, allow the hooks to be used */
159471047Sjulian		hook1->hk_flags &= ~HK_INVALID;
159571047Sjulian		hook2->hk_flags &= ~HK_INVALID;
159671047Sjulian	}
159771047Sjulian	NG_HOOK_UNREF(hook1);
159870939Sjulian	NG_HOOK_UNREF(hook2);
159970939Sjulian	return (error);
160052419Sjulian}
160171047Sjulian
160270700Sjulian/************************************************************************
160370700Sjulian		Utility routines to send self messages
160470700Sjulian************************************************************************/
160570700Sjulian
160671849Sjulian/* Shut this node down as soon as everyone is clear of it */
1607167677Srwatson/* Should add arg "immediately" to jump the queue */
160870700Sjulianint
1609186060Smavng_rmnode_self(node_p node)
161070700Sjulian{
161171849Sjulian	int		error;
161252419Sjulian
161371849Sjulian	if (node == &ng_deadnode)
161471849Sjulian		return (0);
1615132464Sjulian	node->nd_flags |= NGF_INVALID;
1616132464Sjulian	if (node->nd_flags & NGF_CLOSING)
161771849Sjulian		return (0);
161870700Sjulian
1619186060Smav	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
162071849Sjulian	return (error);
162170700Sjulian}
162270700Sjulian
162371849Sjulianstatic void
162471047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
162571047Sjulian{
162671047Sjulian	ng_destroy_hook(hook);
162771849Sjulian	return ;
162871047Sjulian}
162971047Sjulian
163070935Sjulianint
163170935Sjulianng_rmhook_self(hook_p hook)
163270935Sjulian{
163371047Sjulian	int		error;
163470935Sjulian	node_p node = NG_HOOK_NODE(hook);
163570935Sjulian
163671047Sjulian	if (node == &ng_deadnode)
163771047Sjulian		return (0);
163871047Sjulian
163971047Sjulian	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
164071047Sjulian	return (error);
164170935Sjulian}
164270935Sjulian
164370700Sjulian/***********************************************************************
164452419Sjulian * Parse and verify a string of the form:  <NODE:><PATH>
164552419Sjulian *
164652419Sjulian * Such a string can refer to a specific node or a specific hook
164752419Sjulian * on a specific node, depending on how you look at it. In the
164852419Sjulian * latter case, the PATH component must not end in a dot.
164952419Sjulian *
165052419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string
165152419Sjulian * of hook names separated by dots. This breaks out the original
165252419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp
165352419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to
165452419Sjulian * the final hook component of <PATH>, if any, otherwise NULL.
165552419Sjulian *
165652419Sjulian * This returns -1 if the path is malformed. The char ** are optional.
165770700Sjulian ***********************************************************************/
165852419Sjulianint
165952419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
166052419Sjulian{
1661152451Sglebius	char	*node, *path, *hook;
1662152451Sglebius	int	k;
166352419Sjulian
166452419Sjulian	/*
166552419Sjulian	 * Extract absolute NODE, if any
166652419Sjulian	 */
166752419Sjulian	for (path = addr; *path && *path != ':'; path++);
166852419Sjulian	if (*path) {
166952419Sjulian		node = addr;	/* Here's the NODE */
167052419Sjulian		*path++ = '\0';	/* Here's the PATH */
167152419Sjulian
167252419Sjulian		/* Node name must not be empty */
167352419Sjulian		if (!*node)
167452419Sjulian			return -1;
167552419Sjulian
167652419Sjulian		/* A name of "." is OK; otherwise '.' not allowed */
167752419Sjulian		if (strcmp(node, ".") != 0) {
167852419Sjulian			for (k = 0; node[k]; k++)
167952419Sjulian				if (node[k] == '.')
168052419Sjulian					return -1;
168152419Sjulian		}
168252419Sjulian	} else {
168352419Sjulian		node = NULL;	/* No absolute NODE */
168452419Sjulian		path = addr;	/* Here's the PATH */
168552419Sjulian	}
168652419Sjulian
168752419Sjulian	/* Snoop for illegal characters in PATH */
168852419Sjulian	for (k = 0; path[k]; k++)
168952419Sjulian		if (path[k] == ':')
169052419Sjulian			return -1;
169152419Sjulian
169252419Sjulian	/* Check for no repeated dots in PATH */
169352419Sjulian	for (k = 0; path[k]; k++)
169452419Sjulian		if (path[k] == '.' && path[k + 1] == '.')
169552419Sjulian			return -1;
169652419Sjulian
169752419Sjulian	/* Remove extra (degenerate) dots from beginning or end of PATH */
169852419Sjulian	if (path[0] == '.')
169952419Sjulian		path++;
170052419Sjulian	if (*path && path[strlen(path) - 1] == '.')
170152419Sjulian		path[strlen(path) - 1] = 0;
170252419Sjulian
170352419Sjulian	/* If PATH has a dot, then we're not talking about a hook */
170452419Sjulian	if (*path) {
170552419Sjulian		for (hook = path, k = 0; path[k]; k++)
170652419Sjulian			if (path[k] == '.') {
170752419Sjulian				hook = NULL;
170852419Sjulian				break;
170952419Sjulian			}
171052419Sjulian	} else
171152419Sjulian		path = hook = NULL;
171252419Sjulian
171352419Sjulian	/* Done */
171452419Sjulian	if (nodep)
171552419Sjulian		*nodep = node;
171652419Sjulian	if (pathp)
171752419Sjulian		*pathp = path;
171852419Sjulian	if (hookp)
171952419Sjulian		*hookp = hook;
172052419Sjulian	return (0);
172152419Sjulian}
172252419Sjulian
172352419Sjulian/*
172452419Sjulian * Given a path, which may be absolute or relative, and a starting node,
172570700Sjulian * return the destination node.
172652419Sjulian */
172752419Sjulianint
1728229003Sglebiusng_path2noderef(node_p here, const char *address, node_p *destp,
1729229003Sglebius    hook_p *lasthook)
173052419Sjulian{
1731125028Sharti	char    fullpath[NG_PATHSIZ];
1732219827Sglebius	char   *nodename, *path;
173370700Sjulian	node_p  node, oldnode;
173452419Sjulian
173552419Sjulian	/* Initialize */
173670784Sjulian	if (destp == NULL) {
173771047Sjulian		TRAP_ERROR();
173852419Sjulian		return EINVAL;
173970784Sjulian	}
174052419Sjulian	*destp = NULL;
174152419Sjulian
174252419Sjulian	/* Make a writable copy of address for ng_path_parse() */
174352419Sjulian	strncpy(fullpath, address, sizeof(fullpath) - 1);
174452419Sjulian	fullpath[sizeof(fullpath) - 1] = '\0';
174552419Sjulian
174652419Sjulian	/* Parse out node and sequence of hooks */
174752419Sjulian	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
174871047Sjulian		TRAP_ERROR();
174952419Sjulian		return EINVAL;
175052419Sjulian	}
175152419Sjulian
175270700Sjulian	/*
175370700Sjulian	 * For an absolute address, jump to the starting node.
175470700Sjulian	 * Note that this holds a reference on the node for us.
175570700Sjulian	 * Don't forget to drop the reference if we don't need it.
175670700Sjulian	 */
175752419Sjulian	if (nodename) {
175870700Sjulian		node = ng_name2noderef(here, nodename);
175952419Sjulian		if (node == NULL) {
176071047Sjulian			TRAP_ERROR();
176152419Sjulian			return (ENOENT);
176252419Sjulian		}
176370700Sjulian	} else {
176470700Sjulian		if (here == NULL) {
176571047Sjulian			TRAP_ERROR();
176670700Sjulian			return (EINVAL);
176770700Sjulian		}
176852419Sjulian		node = here;
176970784Sjulian		NG_NODE_REF(node);
177070700Sjulian	}
177152419Sjulian
1772219827Sglebius	if (path == NULL) {
1773219827Sglebius		if (lasthook != NULL)
1774219827Sglebius			*lasthook = NULL;
1775219827Sglebius		*destp = node;
1776219827Sglebius		return (0);
1777219827Sglebius	}
1778219827Sglebius
177970700Sjulian	/*
1780152451Sglebius	 * Now follow the sequence of hooks
1781219827Sglebius	 *
1782219827Sglebius	 * XXXGL: The path may demolish as we go the sequence, but if
1783219827Sglebius	 * we hold the topology mutex at critical places, then, I hope,
1784219827Sglebius	 * we would always have valid pointers in hand, although the
1785219827Sglebius	 * path behind us may no longer exist.
178670700Sjulian	 */
1787219827Sglebius	for (;;) {
1788219827Sglebius		hook_p hook;
178952419Sjulian		char *segment;
179052419Sjulian
179152419Sjulian		/*
179252419Sjulian		 * Break out the next path segment. Replace the dot we just
1793219827Sglebius		 * found with a NUL; "path" points to the next segment (or the
179452419Sjulian		 * NUL at the end).
179552419Sjulian		 */
1796219827Sglebius		for (segment = path; *path != '\0'; path++) {
1797219827Sglebius			if (*path == '.') {
1798219827Sglebius				*path++ = '\0';
179952419Sjulian				break;
180052419Sjulian			}
180152419Sjulian		}
180252419Sjulian
180352419Sjulian		/* We have a segment, so look for a hook by that name */
180454096Sarchie		hook = ng_findhook(node, segment);
180552419Sjulian
1806256550Smelifaro		TOPOLOGY_WLOCK();
180752419Sjulian		/* Can't get there from here... */
1808229003Sglebius		if (hook == NULL || NG_HOOK_PEER(hook) == NULL ||
1809229003Sglebius		    NG_HOOK_NOT_VALID(hook) ||
1810229003Sglebius		    NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
181171047Sjulian			TRAP_ERROR();
181270784Sjulian			NG_NODE_UNREF(node);
1813256550Smelifaro			TOPOLOGY_WUNLOCK();
181452419Sjulian			return (ENOENT);
181552419Sjulian		}
181652419Sjulian
181770700Sjulian		/*
1818152451Sglebius		 * Hop on over to the next node
181970700Sjulian		 * XXX
1820152451Sglebius		 * Big race conditions here as hooks and nodes go away
182170700Sjulian		 * *** Idea.. store an ng_ID_t in each hook and use that
182270700Sjulian		 * instead of the direct hook in this crawl?
182370700Sjulian		 */
182470700Sjulian		oldnode = node;
182570784Sjulian		if ((node = NG_PEER_NODE(hook)))
182670784Sjulian			NG_NODE_REF(node);	/* XXX RACE */
182770784Sjulian		NG_NODE_UNREF(oldnode);	/* XXX another race */
182870784Sjulian		if (NG_NODE_NOT_VALID(node)) {
182970784Sjulian			NG_NODE_UNREF(node);	/* XXX more races */
1830256550Smelifaro			TOPOLOGY_WUNLOCK();
1831219827Sglebius			TRAP_ERROR();
1832219827Sglebius			return (ENXIO);
183370700Sjulian		}
183452419Sjulian
1835219827Sglebius		if (*path == '\0') {
1836219827Sglebius			if (lasthook != NULL) {
1837219827Sglebius				if (hook != NULL) {
1838219827Sglebius					*lasthook = NG_HOOK_PEER(hook);
1839219827Sglebius					NG_HOOK_REF(*lasthook);
1840219827Sglebius				} else
1841219827Sglebius					*lasthook = NULL;
1842219827Sglebius			}
1843256550Smelifaro			TOPOLOGY_WUNLOCK();
1844219827Sglebius			*destp = node;
1845219827Sglebius			return (0);
1846219827Sglebius		}
1847256550Smelifaro		TOPOLOGY_WUNLOCK();
184852419Sjulian	}
184952419Sjulian}
185052419Sjulian
185170700Sjulian/***************************************************************\
185270700Sjulian* Input queue handling.
185370700Sjulian* All activities are submitted to the node via the input queue
185470700Sjulian* which implements a multiple-reader/single-writer gate.
1855167677Srwatson* Items which cannot be handled immediately are queued.
185670700Sjulian*
185770700Sjulian* read-write queue locking inline functions			*
185870700Sjulian\***************************************************************/
185970700Sjulian
1860178228Smavstatic __inline void	ng_queue_rw(node_p node, item_p  item, int rw);
1861178228Smavstatic __inline item_p	ng_dequeue(node_p node, int *rw);
1862178228Smavstatic __inline item_p	ng_acquire_read(node_p node, item_p  item);
1863178228Smavstatic __inline item_p	ng_acquire_write(node_p node, item_p  item);
1864178228Smavstatic __inline void	ng_leave_read(node_p node);
1865178228Smavstatic __inline void	ng_leave_write(node_p node);
186670700Sjulian
186752419Sjulian/*
186870700Sjulian * Definition of the bits fields in the ng_queue flag word.
186970700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle
187070700Sjulian * with them.
187170700Sjulian *
187271902Sjulian * The ordering here may be important! don't shuffle these.
187352419Sjulian */
187470700Sjulian/*-
187570700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet)
187670700Sjulian                       |
187770700Sjulian                       V
187870700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
1879151973Sglebius  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
1880151973Sglebius  | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
1881151973Sglebius  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
188270700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
1883151973Sglebius  \___________________________ ____________________________/ | |
1884151973Sglebius                            V                                | |
1885151973Sglebius                  [active reader count]                      | |
188670700Sjulian                                                             | |
1887151973Sglebius            Operation Pending -------------------------------+ |
188870700Sjulian                                                               |
1889151973Sglebius          Active Writer ---------------------------------------+
189071902Sjulian
1891178039SmavNode queue has such semantics:
1892178039Smav- All flags modifications are atomic.
1893178039Smav- Reader count can be incremented only if there is no writer or pending flags.
1894178039Smav  As soon as this can't be done with single operation, it is implemented with
1895178039Smav  spin loop and atomic_cmpset().
1896178039Smav- Writer flag can be set only if there is no any bits set.
1897178039Smav  It is implemented with atomic_cmpset().
1898178039Smav- Pending flag can be set any time, but to avoid collision on queue processing
1899178039Smav  all queue fields are protected by the mutex.
1900178039Smav- Queue processing thread reads queue holding the mutex, but releases it while
1901178039Smav  processing. When queue is empty pending flag is removed.
1902178039Smav*/
190371902Sjulian
1904151973Sglebius#define WRITER_ACTIVE	0x00000001
1905151973Sglebius#define OP_PENDING	0x00000002
1906151973Sglebius#define READER_INCREMENT 0x00000004
1907151973Sglebius#define READER_MASK	0xfffffffc	/* Not valid if WRITER_ACTIVE is set */
1908151973Sglebius#define SAFETY_BARRIER	0x00100000	/* 128K items queued should be enough */
190971902Sjulian
191071902Sjulian/* Defines of more elaborate states on the queue */
1911151973Sglebius/* Mask of bits a new read cares about */
1912151973Sglebius#define NGQ_RMASK	(WRITER_ACTIVE|OP_PENDING)
191371902Sjulian
1914151973Sglebius/* Mask of bits a new write cares about */
191571902Sjulian#define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
191671902Sjulian
1917151973Sglebius/* Test to decide if there is something on the queue. */
1918151973Sglebius#define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
191971902Sjulian
1920151973Sglebius/* How to decide what the next queued item is. */
1921178228Smav#define HEAD_IS_READER(QP)  NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue))
1922178228Smav#define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */
1923151973Sglebius
1924151973Sglebius/* Read the status to decide if the next item on the queue can now run. */
1925151973Sglebius#define QUEUED_READER_CAN_PROCEED(QP)			\
1926151973Sglebius		(((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
1927151973Sglebius#define QUEUED_WRITER_CAN_PROCEED(QP)			\
1928151973Sglebius		(((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1929151973Sglebius
193071902Sjulian/* Is there a chance of getting ANY work off the queue? */
1931151973Sglebius#define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)				\
1932151973Sglebius	((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :		\
1933177953Smav				QUEUED_WRITER_CAN_PROCEED(QP))
193471902Sjulian
1935151238Sglebius#define NGQRW_R 0
1936151238Sglebius#define NGQRW_W 1
1937151238Sglebius
1938178228Smav#define NGQ2_WORKQ	0x00000001
1939178228Smav
194070700Sjulian/*
194170700Sjulian * Taking into account the current state of the queue and node, possibly take
194270700Sjulian * the next entry off the queue and return it. Return NULL if there was
194370700Sjulian * nothing we could return, either because there really was nothing there, or
194470700Sjulian * because the node was in a state where it cannot yet process the next item
194570700Sjulian * on the queue.
194670700Sjulian */
194770700Sjulianstatic __inline item_p
1948178228Smavng_dequeue(node_p node, int *rw)
194970700Sjulian{
195070700Sjulian	item_p item;
1951178228Smav	struct ng_queue *ngq = &node->nd_input_queue;
195271902Sjulian
1953178039Smav	/* This MUST be called with the mutex held. */
1954139039Sglebius	mtx_assert(&ngq->q_mtx, MA_OWNED);
1955178039Smav
1956178039Smav	/* If there is nothing queued, then just return. */
1957151973Sglebius	if (!QUEUE_ACTIVE(ngq)) {
1958154275Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
1959154275Sglebius		    "queue flags 0x%lx", __func__,
1960178228Smav		    node->nd_ID, node, ngq->q_flags);
1961151973Sglebius		return (NULL);
1962151973Sglebius	}
1963139039Sglebius
1964151973Sglebius	/*
1965151973Sglebius	 * From here, we can assume there is a head item.
1966151973Sglebius	 * We need to find out what it is and if it can be dequeued, given
1967151973Sglebius	 * the current state of the node.
1968151973Sglebius	 */
1969151973Sglebius	if (HEAD_IS_READER(ngq)) {
1970177953Smav		while (1) {
1971177953Smav			long t = ngq->q_flags;
1972177953Smav			if (t & WRITER_ACTIVE) {
1973178039Smav				/* There is writer, reader can't proceed. */
1974229003Sglebius				CTR4(KTR_NET, "%20s: node [%x] (%p) queued "
1975229003Sglebius				    "reader can't proceed; queue flags 0x%lx",
1976229003Sglebius				    __func__, node->nd_ID, node, t);
1977177953Smav				return (NULL);
1978177953Smav			}
1979178228Smav			if (atomic_cmpset_acq_int(&ngq->q_flags, t,
1980177953Smav			    t + READER_INCREMENT))
1981177953Smav				break;
1982177953Smav			cpu_spinwait();
1983151973Sglebius		}
1984178039Smav		/* We have got reader lock for the node. */
1985151238Sglebius		*rw = NGQRW_R;
1986178228Smav	} else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING,
1987177953Smav	    OP_PENDING + WRITER_ACTIVE)) {
1988178039Smav		/* We have got writer lock for the node. */
1989151238Sglebius		*rw = NGQRW_W;
199070700Sjulian	} else {
1991178039Smav		/* There is somebody other, writer can't proceed. */
1992229003Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer can't "
1993229003Sglebius		    "proceed; queue flags 0x%lx", __func__, node->nd_ID, node,
1994229003Sglebius		    ngq->q_flags);
1995151973Sglebius		return (NULL);
199670700Sjulian	}
199752419Sjulian
199870700Sjulian	/*
199970700Sjulian	 * Now we dequeue the request (whatever it may be) and correct the
200070700Sjulian	 * pending flags and the next and last pointers.
200170700Sjulian	 */
2002178228Smav	item = STAILQ_FIRST(&ngq->queue);
2003178228Smav	STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
2004178228Smav	if (STAILQ_EMPTY(&ngq->queue))
2005178228Smav		atomic_clear_int(&ngq->q_flags, OP_PENDING);
2006229003Sglebius	CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; queue "
2007229003Sglebius	    "flags 0x%lx", __func__, node->nd_ID, node, item, *rw ? "WRITER" :
2008229003Sglebius	    "READER", ngq->q_flags);
200970700Sjulian	return (item);
201070700Sjulian}
201152419Sjulian
201270700Sjulian/*
2013178039Smav * Queue a packet to be picked up later by someone else.
2014178039Smav * If the queue could be run now, add node to the queue handler's worklist.
201570700Sjulian */
201670700Sjulianstatic __inline void
2017178228Smavng_queue_rw(node_p node, item_p  item, int rw)
201870700Sjulian{
2019178228Smav	struct ng_queue *ngq = &node->nd_input_queue;
2020151973Sglebius	if (rw == NGQRW_W)
2021151973Sglebius		NGI_SET_WRITER(item);
2022151973Sglebius	else
2023151973Sglebius		NGI_SET_READER(item);
2024241009Srstone	item->depth = 1;
2025177953Smav
2026177953Smav	NG_QUEUE_LOCK(ngq);
2027177953Smav	/* Set OP_PENDING flag and enqueue the item. */
2028178228Smav	atomic_set_int(&ngq->q_flags, OP_PENDING);
2029178228Smav	STAILQ_INSERT_TAIL(&ngq->queue, item, el_next);
2030177953Smav
2031154275Sglebius	CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
2032178228Smav	    node->nd_ID, node, item, rw ? "WRITER" : "READER" );
2033229003Sglebius
203470700Sjulian	/*
2035151973Sglebius	 * We can take the worklist lock with the node locked
2036151973Sglebius	 * BUT NOT THE REVERSE!
2037151973Sglebius	 */
2038151973Sglebius	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2039178228Smav		ng_worklist_add(node);
2040177953Smav	NG_QUEUE_UNLOCK(ngq);
204170700Sjulian}
204252419Sjulian
2043178039Smav/* Acquire reader lock on node. If node is busy, queue the packet. */
204470700Sjulianstatic __inline item_p
2045178228Smavng_acquire_read(node_p node, item_p item)
204652419Sjulian{
2047178228Smav	KASSERT(node != &ng_deadnode,
2048151974Sglebius	    ("%s: working on deadnode", __func__));
204952419Sjulian
2050177953Smav	/* Reader needs node without writer and pending items. */
2051229003Sglebius	for (;;) {
2052178228Smav		long t = node->nd_input_queue.q_flags;
2053177953Smav		if (t & NGQ_RMASK)
2054177953Smav			break; /* Node is not ready for reader. */
2055229003Sglebius		if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, t,
2056229003Sglebius		    t + READER_INCREMENT)) {
2057177953Smav	    		/* Successfully grabbed node */
2058177953Smav			CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
2059178228Smav			    __func__, node->nd_ID, node, item);
2060177953Smav			return (item);
2061177953Smav		}
2062177953Smav		cpu_spinwait();
2063297793Spfg	}
206470700Sjulian
2065177953Smav	/* Queue the request for later. */
2066178228Smav	ng_queue_rw(node, item, NGQRW_R);
206770700Sjulian
2068148236Sglebius	return (NULL);
206970700Sjulian}
207070700Sjulian
2071178039Smav/* Acquire writer lock on node. If node is busy, queue the packet. */
207270700Sjulianstatic __inline item_p
2073178228Smavng_acquire_write(node_p node, item_p item)
207470700Sjulian{
2075178228Smav	KASSERT(node != &ng_deadnode,
2076151974Sglebius	    ("%s: working on deadnode", __func__));
2077151974Sglebius
2078177953Smav	/* Writer needs completely idle node. */
2079229003Sglebius	if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, 0,
2080229003Sglebius	    WRITER_ACTIVE)) {
2081177953Smav	    	/* Successfully grabbed node */
2082154275Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
2083178228Smav		    __func__, node->nd_ID, node, item);
208470700Sjulian		return (item);
208552419Sjulian	}
208652419Sjulian
2087177953Smav	/* Queue the request for later. */
2088178228Smav	ng_queue_rw(node, item, NGQRW_W);
208970700Sjulian
2090148236Sglebius	return (NULL);
209170700Sjulian}
209270700Sjulian
2093167385Sjulian#if 0
2094167385Sjulianstatic __inline item_p
2095178228Smavng_upgrade_write(node_p node, item_p item)
2096167385Sjulian{
2097178228Smav	struct ng_queue *ngq = &node->nd_input_queue;
2098178228Smav	KASSERT(node != &ng_deadnode,
2099167385Sjulian	    ("%s: working on deadnode", __func__));
2100167385Sjulian
2101167385Sjulian	NGI_SET_WRITER(item);
2102167385Sjulian
2103178228Smav	NG_QUEUE_LOCK(ngq);
2104167385Sjulian
2105167385Sjulian	/*
2106167385Sjulian	 * There will never be no readers as we are there ourselves.
2107167385Sjulian	 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2108167385Sjulian	 * The caller we are running from will call ng_leave_read()
2109167385Sjulian	 * soon, so we must account for that. We must leave again with the
2110167385Sjulian	 * READER lock. If we find other readers, then
2111167385Sjulian	 * queue the request for later. However "later" may be rignt now
2112167385Sjulian	 * if there are no readers. We don't really care if there are queued
2113167385Sjulian	 * items as we will bypass them anyhow.
2114167385Sjulian	 */
2115178228Smav	atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
2116178228Smav	if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) {
2117178228Smav		NG_QUEUE_UNLOCK(ngq);
2118167385Sjulian
2119167385Sjulian		/* It's just us, act on the item. */
2120167385Sjulian		/* will NOT drop writer lock when done */
2121167385Sjulian		ng_apply_item(node, item, 0);
2122167385Sjulian
2123167385Sjulian		/*
2124229003Sglebius		 * Having acted on the item, atomically
2125229003Sglebius		 * downgrade back to READER and finish up.
2126167385Sjulian	 	 */
2127229003Sglebius		atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2128167385Sjulian
2129167385Sjulian		/* Our caller will call ng_leave_read() */
2130167385Sjulian		return;
2131167385Sjulian	}
2132167385Sjulian	/*
2133167385Sjulian	 * It's not just us active, so queue us AT THE HEAD.
2134167385Sjulian	 * "Why?" I hear you ask.
2135167385Sjulian	 * Put us at the head of the queue as we've already been
2136167385Sjulian	 * through it once. If there is nothing else waiting,
2137167385Sjulian	 * set the correct flags.
2138167385Sjulian	 */
2139178228Smav	if (STAILQ_EMPTY(&ngq->queue)) {
2140167385Sjulian		/* We've gone from, 0 to 1 item in the queue */
2141178228Smav		atomic_set_int(&ngq->q_flags, OP_PENDING);
2142167385Sjulian
2143167385Sjulian		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
2144178228Smav		    node->nd_ID, node);
2145167385Sjulian	};
2146178228Smav	STAILQ_INSERT_HEAD(&ngq->queue, item, el_next);
2147178228Smav	CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
2148178228Smav	    __func__, node->nd_ID, node, item );
2149167385Sjulian
2150167385Sjulian	/* Reverse what we did above. That downgrades us back to reader */
2151178228Smav	atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2152177953Smav	if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2153178228Smav		ng_worklist_add(node);
2154178228Smav	NG_QUEUE_UNLOCK(ngq);
2155167385Sjulian
2156167385Sjulian	return;
2157167385Sjulian}
2158167385Sjulian#endif
2159167385Sjulian
2160178039Smav/* Release reader lock. */
216170700Sjulianstatic __inline void
2162178228Smavng_leave_read(node_p node)
216370700Sjulian{
2164178228Smav	atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT);
216570700Sjulian}
216670700Sjulian
2167178039Smav/* Release writer lock. */
216870700Sjulianstatic __inline void
2169178228Smavng_leave_write(node_p node)
217070700Sjulian{
2171178228Smav	atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE);
217270700Sjulian}
217370700Sjulian
2174178039Smav/* Purge node queue. Called on node shutdown. */
217570700Sjulianstatic void
2176178228Smavng_flush_input_queue(node_p node)
217770700Sjulian{
2178178228Smav	struct ng_queue *ngq = &node->nd_input_queue;
217970700Sjulian	item_p item;
2180151973Sglebius
2181168049Swkoszek	NG_QUEUE_LOCK(ngq);
2182178228Smav	while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) {
2183178228Smav		STAILQ_REMOVE_HEAD(&ngq->queue, el_next);
2184178228Smav		if (STAILQ_EMPTY(&ngq->queue))
2185178228Smav			atomic_clear_int(&ngq->q_flags, OP_PENDING);
2186168049Swkoszek		NG_QUEUE_UNLOCK(ngq);
218770700Sjulian
2188151973Sglebius		/* If the item is supplying a callback, call it with an error */
2189177071Smav		if (item->apply != NULL) {
2190177071Smav			if (item->depth == 1)
2191177071Smav				item->apply->error = ENOENT;
2192177071Smav			if (refcount_release(&item->apply->refs)) {
2193177071Smav				(*item->apply->apply)(item->apply->context,
2194177071Smav				    item->apply->error);
2195177071Smav			}
2196151283Sglebius		}
2197132369Sjulian		NG_FREE_ITEM(item);
2198168049Swkoszek		NG_QUEUE_LOCK(ngq);
219970700Sjulian	}
2200168049Swkoszek	NG_QUEUE_UNLOCK(ngq);
220170700Sjulian}
220270700Sjulian
220370700Sjulian/***********************************************************************
220470700Sjulian* Externally visible method for sending or queueing messages or data.
220570700Sjulian***********************************************************************/
220670700Sjulian
220770700Sjulian/*
220871849Sjulian * The module code should have filled out the item correctly by this stage:
220970700Sjulian * Common:
221070700Sjulian *    reference to destination node.
221170700Sjulian *    Reference to destination rcv hook if relevant.
2212172806Smav *    apply pointer must be or NULL or reference valid struct ng_apply_info.
221370700Sjulian * Data:
221470700Sjulian *    pointer to mbuf
221570700Sjulian * Control_Message:
221670700Sjulian *    pointer to msg.
221770700Sjulian *    ID of original sender node. (return address)
221871849Sjulian * Function:
221971849Sjulian *    Function pointer
222071849Sjulian *    void * argument
222171849Sjulian *    integer argument
222270700Sjulian *
222370700Sjulian * The nodes have several routines and macros to help with this task:
222470700Sjulian */
222570700Sjulian
222670700Sjulianint
2227146281Sglebiusng_snd_item(item_p item, int flags)
222870700Sjulian{
2229172806Smav	hook_p hook;
2230172806Smav	node_p node;
2231146281Sglebius	int queue, rw;
2232172806Smav	struct ng_queue *ngq;
2233151256Sglebius	int error = 0;
223470700Sjulian
2235176046Smav	/* We are sending item, so it must be present! */
2236176046Smav	KASSERT(item != NULL, ("ng_snd_item: item is NULL"));
2237172806Smav
223870784Sjulian#ifdef	NETGRAPH_DEBUG
2239152451Sglebius	_ngi_check(item, __FILE__, __LINE__);
224070700Sjulian#endif
224170700Sjulian
2242176046Smav	/* Item was sent once more, postpone apply() call. */
2243172806Smav	if (item->apply)
2244172806Smav		refcount_acquire(&item->apply->refs);
2245146281Sglebius
2246172806Smav	node = NGI_NODE(item);
2247176046Smav	/* Node is never optional. */
2248176046Smav	KASSERT(node != NULL, ("ng_snd_item: node is NULL"));
2249172806Smav
2250175850Smav	hook = NGI_HOOK(item);
2251176046Smav	/* Valid hook and mbuf are mandatory for data. */
2252176046Smav	if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) {
2253176046Smav		KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL"));
2254131123Sjulian		if (NGI_M(item) == NULL)
2255172806Smav			ERROUT(EINVAL);
225670700Sjulian		CHECK_DATA_MBUF(NGI_M(item));
225769922Sjulian	}
2258149505Sglebius
225970700Sjulian	/*
2260176046Smav	 * If the item or the node specifies single threading, force
2261176046Smav	 * writer semantics. Similarly, the node may say one hook always
2262176046Smav	 * produces writers. These are overrides.
226370700Sjulian	 */
2264176567Smav	if (((item->el_flags & NGQF_RW) == NGQF_WRITER) ||
2265176046Smav	    (node->nd_flags & NGF_FORCE_WRITER) ||
2266176046Smav	    (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
2267176046Smav		rw = NGQRW_W;
2268176046Smav	} else {
2269176046Smav		rw = NGQRW_R;
2270176046Smav	}
2271149505Sglebius
2272175847Smav	/*
2273194012Szec	 * If sender or receiver requests queued delivery, or call graph
2274194012Szec	 * loops back from outbound to inbound path, or stack usage
2275175847Smav	 * level is dangerous - enqueue message.
2276175847Smav	 */
2277175847Smav	if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) {
2278175847Smav		queue = 1;
2279194012Szec	} else if (hook && (hook->hk_flags & HK_TO_INBOUND) &&
2280194012Szec	    curthread->td_ng_outbound) {
2281194012Szec		queue = 1;
2282176046Smav	} else {
2283176046Smav		queue = 0;
2284175847Smav#ifdef GET_STACK_USAGE
2285175868Smav		/*
2286175871Smarck		 * Most of netgraph nodes have small stack consumption and
2287176046Smav		 * for them 25% of free stack space is more than enough.
2288175868Smav		 * Nodes/hooks with higher stack usage should be marked as
2289175889Smarck		 * HI_STACK. For them 50% of stack will be guaranteed then.
2290176046Smav		 * XXX: Values 25% and 50% are completely empirical.
2291175868Smav		 */
2292176046Smav		size_t	st, su, sl;
2293175847Smav		GET_STACK_USAGE(st, su);
2294176046Smav		sl = st - su;
2295229003Sglebius		if ((sl * 4 < st) || ((sl * 2 < st) &&
2296229003Sglebius		    ((node->nd_flags & NGF_HI_STACK) || (hook &&
2297229003Sglebius		    (hook->hk_flags & HK_HI_STACK)))))
2298175847Smav			queue = 1;
2299176046Smav#endif
2300175847Smav	}
2301175847Smav
230270700Sjulian	if (queue) {
230370700Sjulian		/* Put it on the queue for that node*/
2304178228Smav		ng_queue_rw(node, item, rw);
2305176046Smav		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
230670700Sjulian	}
230769922Sjulian
230870700Sjulian	/*
230970700Sjulian	 * We already decided how we will be queueud or treated.
231070700Sjulian	 * Try get the appropriate operating permission.
231170700Sjulian	 */
2312151256Sglebius 	if (rw == NGQRW_R)
2313178228Smav		item = ng_acquire_read(node, item);
2314151256Sglebius	else
2315178228Smav		item = ng_acquire_write(node, item);
231652419Sjulian
2317176046Smav	/* Item was queued while trying to get permission. */
2318176046Smav	if (item == NULL)
2319176046Smav		return ((flags & NG_PROGRESS) ? EINPROGRESS : 0);
232052419Sjulian
232174078Sjulian	NGI_GET_NODE(item, node); /* zaps stored node */
232252419Sjulian
2323177071Smav	item->depth++;
2324170180Sglebius	error = ng_apply_item(node, item, rw); /* drops r/w lock when done */
232574078Sjulian
2326177953Smav	/* If something is waiting on queue and ready, schedule it. */
2327178228Smav	ngq = &node->nd_input_queue;
2328177953Smav	if (QUEUE_ACTIVE(ngq)) {
2329177953Smav		NG_QUEUE_LOCK(ngq);
2330177953Smav		if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2331178228Smav			ng_worklist_add(node);
2332177953Smav		NG_QUEUE_UNLOCK(ngq);
2333177953Smav	}
2334177953Smav
233552419Sjulian	/*
2336177953Smav	 * Node may go away as soon as we remove the reference.
2337177953Smav	 * Whatever we do, DO NOT access the node again!
233874078Sjulian	 */
2339177953Smav	NG_NODE_UNREF(node);
234074078Sjulian
234171849Sjulian	return (error);
2342172806Smav
2343172806Smavdone:
2344176046Smav	/* If was not sent, apply callback here. */
2345177071Smav	if (item->apply != NULL) {
2346177071Smav		if (item->depth == 0 && error != 0)
2347177071Smav			item->apply->error = error;
2348177071Smav		if (refcount_release(&item->apply->refs)) {
2349177071Smav			(*item->apply->apply)(item->apply->context,
2350177071Smav			    item->apply->error);
2351177071Smav		}
2352177071Smav	}
2353175850Smav
2354172806Smav	NG_FREE_ITEM(item);
2355172806Smav	return (error);
235652419Sjulian}
235752419Sjulian
235852419Sjulian/*
235970700Sjulian * We have an item that was possibly queued somewhere.
236070700Sjulian * It should contain all the information needed
236170700Sjulian * to run it on the appropriate node/hook.
2362172806Smav * If there is apply pointer and we own the last reference, call apply().
236352419Sjulian */
2364170180Sglebiusstatic int
2365151238Sglebiusng_apply_item(node_p node, item_p item, int rw)
236652419Sjulian{
236770700Sjulian	hook_p  hook;
236870700Sjulian	ng_rcvdata_t *rcvdata;
236971885Sjulian	ng_rcvmsg_t *rcvmsg;
2370172806Smav	struct ng_apply_info *apply;
2371177071Smav	int	error = 0, depth;
237252419Sjulian
2373176046Smav	/* Node and item are never optional. */
2374176046Smav	KASSERT(node != NULL, ("ng_apply_item: node is NULL"));
2375176046Smav	KASSERT(item != NULL, ("ng_apply_item: item is NULL"));
2376176046Smav
237771849Sjulian	NGI_GET_HOOK(item, hook); /* clears stored hook */
237870784Sjulian#ifdef	NETGRAPH_DEBUG
2379152451Sglebius	_ngi_check(item, __FILE__, __LINE__);
238070700Sjulian#endif
2381147774Sglebius
2382172806Smav	apply = item->apply;
2383177071Smav	depth = item->depth;
2384147774Sglebius
238571047Sjulian	switch (item->el_flags & NGQF_TYPE) {
238670700Sjulian	case NGQF_DATA:
238770700Sjulian		/*
238870700Sjulian		 * Check things are still ok as when we were queued.
238970700Sjulian		 */
2390176046Smav		KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL"));
2391176046Smav		if (NG_HOOK_NOT_VALID(hook) ||
2392176046Smav		    NG_NODE_NOT_VALID(node)) {
2393167402Sjulian			error = EIO;
239470700Sjulian			NG_FREE_ITEM(item);
239571885Sjulian			break;
239670700Sjulian		}
239771885Sjulian		/*
239871885Sjulian		 * If no receive method, just silently drop it.
2399229003Sglebius		 * Give preference to the hook over-ride method.
240071885Sjulian		 */
2401229003Sglebius		if ((!(rcvdata = hook->hk_rcvdata)) &&
2402229003Sglebius		    (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
240371885Sjulian			error = 0;
240471885Sjulian			NG_FREE_ITEM(item);
240571885Sjulian			break;
240671885Sjulian		}
2407167402Sjulian		error = (*rcvdata)(hook, item);
240870700Sjulian		break;
240970700Sjulian	case NGQF_MESG:
2410175850Smav		if (hook && NG_HOOK_NOT_VALID(hook)) {
2411175850Smav			/*
2412175850Smav			 * The hook has been zapped then we can't use it.
2413175850Smav			 * Immediately drop its reference.
2414175850Smav			 * The message may not need it.
2415175850Smav			 */
2416175850Smav			NG_HOOK_UNREF(hook);
2417175850Smav			hook = NULL;
241870700Sjulian		}
241970700Sjulian		/*
242070700Sjulian		 * Similarly, if the node is a zombie there is
242170700Sjulian		 * nothing we can do with it, drop everything.
242270700Sjulian		 */
242370784Sjulian		if (NG_NODE_NOT_VALID(node)) {
242471047Sjulian			TRAP_ERROR();
2425167402Sjulian			error = EINVAL;
242670700Sjulian			NG_FREE_ITEM(item);
2427175850Smav			break;
242870700Sjulian		}
2429175850Smav		/*
2430175850Smav		 * Call the appropriate message handler for the object.
2431175850Smav		 * It is up to the message handler to free the message.
2432175850Smav		 * If it's a generic message, handle it generically,
2433175850Smav		 * otherwise call the type's message handler (if it exists).
2434175850Smav		 * XXX (race). Remember that a queued message may
2435175850Smav		 * reference a node or hook that has just been
2436175850Smav		 * invalidated. It will exist as the queue code
2437175850Smav		 * is holding a reference, but..
2438175850Smav		 */
2439175850Smav		if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) &&
2440175850Smav		    ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) {
2441175850Smav			error = ng_generic_msg(node, item, hook);
2442175850Smav			break;
2443175850Smav		}
2444175850Smav		if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) &&
2445175850Smav		    (!(rcvmsg = node->nd_type->rcvmsg))) {
2446175850Smav			TRAP_ERROR();
2447175850Smav			error = 0;
2448175850Smav			NG_FREE_ITEM(item);
2449175850Smav			break;
2450175850Smav		}
2451175850Smav		error = (*rcvmsg)(node, item, hook);
245270700Sjulian		break;
245371047Sjulian	case NGQF_FN:
2454173605Sglebius	case NGQF_FN2:
245571047Sjulian		/*
2456182995Smav		 * In the case of the shutdown message we allow it to hit
245771849Sjulian		 * even if the node is invalid.
245871047Sjulian		 */
2459182995Smav		if (NG_NODE_NOT_VALID(node) &&
2460182995Smav		    NGI_FN(item) != &ng_rmnode) {
246171047Sjulian			TRAP_ERROR();
2462167402Sjulian			error = EINVAL;
2463143384Sglebius			NG_FREE_ITEM(item);
246471047Sjulian			break;
246571047Sjulian		}
2466182995Smav		/* Same is about some internal functions and invalid hook. */
2467182995Smav		if (hook && NG_HOOK_NOT_VALID(hook) &&
2468182995Smav		    NGI_FN2(item) != &ng_con_part2 &&
2469182995Smav		    NGI_FN2(item) != &ng_con_part3 &&
2470182995Smav		    NGI_FN(item) != &ng_rmhook_part2) {
2471182995Smav			TRAP_ERROR();
2472182995Smav			error = EINVAL;
2473182995Smav			NG_FREE_ITEM(item);
2474182995Smav			break;
2475182995Smav		}
2476182995Smav
2477173605Sglebius		if ((item->el_flags & NGQF_TYPE) == NGQF_FN) {
2478173605Sglebius			(*NGI_FN(item))(node, hook, NGI_ARG1(item),
2479173605Sglebius			    NGI_ARG2(item));
2480172806Smav			NG_FREE_ITEM(item);
2481173605Sglebius		} else	/* it is NGQF_FN2 */
2482173605Sglebius			error = (*NGI_FN2(item))(node, item, hook);
2483172806Smav		break;
248470700Sjulian	}
248570700Sjulian	/*
248670700Sjulian	 * We held references on some of the resources
248770700Sjulian	 * that we took from the item. Now that we have
248870700Sjulian	 * finished doing everything, drop those references.
248970700Sjulian	 */
2490175850Smav	if (hook)
249170784Sjulian		NG_HOOK_UNREF(hook);
249270700Sjulian
2493176046Smav 	if (rw == NGQRW_R)
2494178228Smav		ng_leave_read(node);
2495176046Smav	else
2496178228Smav		ng_leave_write(node);
2497147774Sglebius
2498147774Sglebius	/* Apply callback. */
2499177071Smav	if (apply != NULL) {
2500177071Smav		if (depth == 1 && error != 0)
2501177071Smav			apply->error = error;
2502177071Smav		if (refcount_release(&apply->refs))
2503177071Smav			(*apply->apply)(apply->context, apply->error);
2504177071Smav	}
2505147774Sglebius
2506170180Sglebius	return (error);
250770700Sjulian}
250870700Sjulian
250970700Sjulian/***********************************************************************
251070700Sjulian * Implement the 'generic' control messages
251170700Sjulian ***********************************************************************/
251270700Sjulianstatic int
251370700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook)
251470700Sjulian{
251570700Sjulian	int error = 0;
251670700Sjulian	struct ng_mesg *msg;
251770700Sjulian	struct ng_mesg *resp = NULL;
251870700Sjulian
251970700Sjulian	NGI_GET_MSG(item, msg);
252052419Sjulian	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
252171047Sjulian		TRAP_ERROR();
252270700Sjulian		error = EINVAL;
252370700Sjulian		goto out;
252452419Sjulian	}
252552419Sjulian	switch (msg->header.cmd) {
252652419Sjulian	case NGM_SHUTDOWN:
252771849Sjulian		ng_rmnode(here, NULL, NULL, 0);
252852419Sjulian		break;
252952419Sjulian	case NGM_MKPEER:
253052419Sjulian	    {
253152419Sjulian		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
253252419Sjulian
253352419Sjulian		if (msg->header.arglen != sizeof(*mkp)) {
253471047Sjulian			TRAP_ERROR();
253570700Sjulian			error = EINVAL;
253670700Sjulian			break;
253752419Sjulian		}
253852419Sjulian		mkp->type[sizeof(mkp->type) - 1] = '\0';
253952419Sjulian		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
254052419Sjulian		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
254152419Sjulian		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
254252419Sjulian		break;
254352419Sjulian	    }
254452419Sjulian	case NGM_CONNECT:
254552419Sjulian	    {
254652419Sjulian		struct ngm_connect *const con =
254752419Sjulian			(struct ngm_connect *) msg->data;
254852419Sjulian		node_p node2;
254952419Sjulian
255052419Sjulian		if (msg->header.arglen != sizeof(*con)) {
255171047Sjulian			TRAP_ERROR();
255270700Sjulian			error = EINVAL;
255370700Sjulian			break;
255452419Sjulian		}
255552419Sjulian		con->path[sizeof(con->path) - 1] = '\0';
255652419Sjulian		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
255752419Sjulian		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
255870700Sjulian		/* Don't forget we get a reference.. */
255970700Sjulian		error = ng_path2noderef(here, con->path, &node2, NULL);
256052419Sjulian		if (error)
256152419Sjulian			break;
2562172806Smav		error = ng_con_nodes(item, here, con->ourhook,
2563172806Smav		    node2, con->peerhook);
256470784Sjulian		NG_NODE_UNREF(node2);
256552419Sjulian		break;
256652419Sjulian	    }
256752419Sjulian	case NGM_NAME:
256852419Sjulian	    {
256952419Sjulian		struct ngm_name *const nam = (struct ngm_name *) msg->data;
257052419Sjulian
257152419Sjulian		if (msg->header.arglen != sizeof(*nam)) {
257271047Sjulian			TRAP_ERROR();
257370700Sjulian			error = EINVAL;
257470700Sjulian			break;
257552419Sjulian		}
257652419Sjulian		nam->name[sizeof(nam->name) - 1] = '\0';
257752419Sjulian		error = ng_name_node(here, nam->name);
257852419Sjulian		break;
257952419Sjulian	    }
258052419Sjulian	case NGM_RMHOOK:
258152419Sjulian	    {
258252419Sjulian		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
258352419Sjulian		hook_p hook;
258452419Sjulian
258552419Sjulian		if (msg->header.arglen != sizeof(*rmh)) {
258671047Sjulian			TRAP_ERROR();
258770700Sjulian			error = EINVAL;
258870700Sjulian			break;
258952419Sjulian		}
259052419Sjulian		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
259154096Sarchie		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
259252419Sjulian			ng_destroy_hook(hook);
259352419Sjulian		break;
259452419Sjulian	    }
259552419Sjulian	case NGM_NODEINFO:
259652419Sjulian	    {
259752419Sjulian		struct nodeinfo *ni;
259852419Sjulian
259970700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
260052419Sjulian		if (resp == NULL) {
260152419Sjulian			error = ENOMEM;
260252419Sjulian			break;
260352419Sjulian		}
260452419Sjulian
260552419Sjulian		/* Fill in node info */
260670700Sjulian		ni = (struct nodeinfo *) resp->data;
260770784Sjulian		if (NG_NODE_HAS_NAME(here))
2608125028Sharti			strcpy(ni->name, NG_NODE_NAME(here));
2609125028Sharti		strcpy(ni->type, here->nd_type->name);
261052722Sjulian		ni->id = ng_node2ID(here);
261170784Sjulian		ni->hooks = here->nd_numhooks;
261252419Sjulian		break;
261352419Sjulian	    }
261452419Sjulian	case NGM_LISTHOOKS:
261552419Sjulian	    {
261670784Sjulian		const int nhooks = here->nd_numhooks;
261752419Sjulian		struct hooklist *hl;
261852419Sjulian		struct nodeinfo *ni;
261952419Sjulian		hook_p hook;
262052419Sjulian
262152419Sjulian		/* Get response struct */
2622229003Sglebius		NG_MKRESPONSE(resp, msg, sizeof(*hl) +
2623229003Sglebius		    (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
262452419Sjulian		if (resp == NULL) {
262552419Sjulian			error = ENOMEM;
262652419Sjulian			break;
262752419Sjulian		}
262870700Sjulian		hl = (struct hooklist *) resp->data;
262952419Sjulian		ni = &hl->nodeinfo;
263052419Sjulian
263152419Sjulian		/* Fill in node info */
263270784Sjulian		if (NG_NODE_HAS_NAME(here))
2633125028Sharti			strcpy(ni->name, NG_NODE_NAME(here));
2634125028Sharti		strcpy(ni->type, here->nd_type->name);
263552722Sjulian		ni->id = ng_node2ID(here);
263652419Sjulian
263752419Sjulian		/* Cycle through the linked list of hooks */
263852419Sjulian		ni->hooks = 0;
263970784Sjulian		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
264052419Sjulian			struct linkinfo *const link = &hl->link[ni->hooks];
264152419Sjulian
264252419Sjulian			if (ni->hooks >= nhooks) {
264352419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
264487599Sobrien				    __func__, "hooks");
264552419Sjulian				break;
264652419Sjulian			}
264770784Sjulian			if (NG_HOOK_NOT_VALID(hook))
264852419Sjulian				continue;
2649125028Sharti			strcpy(link->ourhook, NG_HOOK_NAME(hook));
2650125028Sharti			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
265170784Sjulian			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
2652125028Sharti				strcpy(link->nodeinfo.name,
2653125028Sharti				    NG_PEER_NODE_NAME(hook));
2654125028Sharti			strcpy(link->nodeinfo.type,
2655125028Sharti			   NG_PEER_NODE(hook)->nd_type->name);
265670784Sjulian			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
265770784Sjulian			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
265852419Sjulian			ni->hooks++;
265952419Sjulian		}
266052419Sjulian		break;
266152419Sjulian	    }
266252419Sjulian
266352419Sjulian	case NGM_LISTNODES:
266452419Sjulian	    {
266552419Sjulian		struct namelist *nl;
266652419Sjulian		node_p node;
2667231831Sglebius		int i;
266852419Sjulian
2669231831Sglebius		IDHASH_RLOCK();
2670231831Sglebius		/* Get response struct. */
2671231831Sglebius		NG_MKRESPONSE(resp, msg, sizeof(*nl) +
2672338333Smav		    (V_ng_nodes * sizeof(struct nodeinfo)), M_NOWAIT);
2673231831Sglebius		if (resp == NULL) {
2674231831Sglebius			IDHASH_RUNLOCK();
2675231831Sglebius			error = ENOMEM;
2676231831Sglebius			break;
2677231831Sglebius		}
2678231831Sglebius		nl = (struct namelist *) resp->data;
2679231831Sglebius
2680231831Sglebius		/* Cycle through the lists of nodes. */
2681231831Sglebius		nl->numnames = 0;
2682231831Sglebius		for (i = 0; i <= V_ng_ID_hmask; i++) {
2683231831Sglebius			LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) {
2684231831Sglebius				struct nodeinfo *const np =
2685231831Sglebius				    &nl->nodeinfo[nl->numnames];
2686231831Sglebius
2687231831Sglebius				if (NG_NODE_NOT_VALID(node))
2688231831Sglebius					continue;
2689231831Sglebius				if (NG_NODE_HAS_NAME(node))
2690231831Sglebius					strcpy(np->name, NG_NODE_NAME(node));
2691231831Sglebius				strcpy(np->type, node->nd_type->name);
2692231831Sglebius				np->id = ng_node2ID(node);
2693231831Sglebius				np->hooks = node->nd_numhooks;
2694231831Sglebius				KASSERT(nl->numnames < V_ng_nodes,
2695231831Sglebius				    ("%s: no space", __func__));
2696231831Sglebius				nl->numnames++;
269770912Sjulian			}
269852419Sjulian		}
2699231831Sglebius		IDHASH_RUNLOCK();
2700231831Sglebius		break;
2701231831Sglebius	    }
2702231831Sglebius	case NGM_LISTNAMES:
2703231831Sglebius	    {
2704231831Sglebius		struct namelist *nl;
2705231831Sglebius		node_p node;
2706231831Sglebius		int i;
270752419Sjulian
2708231831Sglebius		NAMEHASH_RLOCK();
2709231831Sglebius		/* Get response struct. */
2710229003Sglebius		NG_MKRESPONSE(resp, msg, sizeof(*nl) +
2711231831Sglebius		    (V_ng_named_nodes * sizeof(struct nodeinfo)), M_NOWAIT);
271252419Sjulian		if (resp == NULL) {
2713230480Sglebius			NAMEHASH_RUNLOCK();
271452419Sjulian			error = ENOMEM;
271552419Sjulian			break;
271652419Sjulian		}
271770700Sjulian		nl = (struct namelist *) resp->data;
271852419Sjulian
2719231831Sglebius		/* Cycle through the lists of nodes. */
272052419Sjulian		nl->numnames = 0;
2721231831Sglebius		for (i = 0; i <= V_ng_name_hmask; i++) {
2722181803Sbz			LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) {
2723176802Smav				struct nodeinfo *const np =
2724176802Smav				    &nl->nodeinfo[nl->numnames];
272552419Sjulian
2726176802Smav				if (NG_NODE_NOT_VALID(node))
2727176802Smav					continue;
2728231831Sglebius				strcpy(np->name, NG_NODE_NAME(node));
2729176802Smav				strcpy(np->type, node->nd_type->name);
2730176802Smav				np->id = ng_node2ID(node);
2731176802Smav				np->hooks = node->nd_numhooks;
2732231831Sglebius				KASSERT(nl->numnames < V_ng_named_nodes,
2733231831Sglebius				    ("%s: no space", __func__));
2734176802Smav				nl->numnames++;
273552419Sjulian			}
273652419Sjulian		}
2737230480Sglebius		NAMEHASH_RUNLOCK();
273852419Sjulian		break;
273952419Sjulian	    }
274052419Sjulian
274152419Sjulian	case NGM_LISTTYPES:
274252419Sjulian	    {
274352419Sjulian		struct typelist *tl;
274452419Sjulian		struct ng_type *type;
274552419Sjulian		int num = 0;
274652419Sjulian
2747230480Sglebius		TYPELIST_RLOCK();
274852419Sjulian		/* Count number of types */
2749230480Sglebius		LIST_FOREACH(type, &ng_typelist, types)
275052419Sjulian			num++;
275152419Sjulian
275252419Sjulian		/* Get response struct */
2753229003Sglebius		NG_MKRESPONSE(resp, msg, sizeof(*tl) +
2754229003Sglebius		    (num * sizeof(struct typeinfo)), M_NOWAIT);
275552419Sjulian		if (resp == NULL) {
2756230480Sglebius			TYPELIST_RUNLOCK();
275752419Sjulian			error = ENOMEM;
275852419Sjulian			break;
275952419Sjulian		}
276070700Sjulian		tl = (struct typelist *) resp->data;
276152419Sjulian
276252419Sjulian		/* Cycle through the linked list of types */
276352419Sjulian		tl->numtypes = 0;
276470700Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
276552419Sjulian			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
276652419Sjulian
2767125028Sharti			strcpy(tp->type_name, type->name);
276871603Sjulian			tp->numnodes = type->refs - 1; /* don't count list */
2769230480Sglebius			KASSERT(tl->numtypes < num, ("%s: no space", __func__));
277052419Sjulian			tl->numtypes++;
277152419Sjulian		}
2772230480Sglebius		TYPELIST_RUNLOCK();
277352419Sjulian		break;
277452419Sjulian	    }
277552419Sjulian
277653913Sarchie	case NGM_BINARY2ASCII:
277753913Sarchie	    {
2778369764Sdonner		int bufSize = 1024;
277953913Sarchie		const struct ng_parse_type *argstype;
278053913Sarchie		const struct ng_cmdlist *c;
278170700Sjulian		struct ng_mesg *binary, *ascii;
278253913Sarchie
278353913Sarchie		/* Data area must contain a valid netgraph message */
278453913Sarchie		binary = (struct ng_mesg *)msg->data;
2785152451Sglebius		if (msg->header.arglen < sizeof(struct ng_mesg) ||
2786152451Sglebius		    (msg->header.arglen - sizeof(struct ng_mesg) <
2787152451Sglebius		    binary->header.arglen)) {
278871047Sjulian			TRAP_ERROR();
278953913Sarchie			error = EINVAL;
279053913Sarchie			break;
279153913Sarchie		}
2792369764Sdonnerretry_b2a:
279370700Sjulian		/* Get a response message with lots of room */
279470700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
279570159Sjulian		if (resp == NULL) {
279653913Sarchie			error = ENOMEM;
279753913Sarchie			break;
279853913Sarchie		}
279970700Sjulian		ascii = (struct ng_mesg *)resp->data;
280053913Sarchie
280153913Sarchie		/* Copy binary message header to response message payload */
280253913Sarchie		bcopy(binary, ascii, sizeof(*binary));
280353913Sarchie
280453913Sarchie		/* Find command by matching typecookie and command number */
2805229003Sglebius		for (c = here->nd_type->cmdlist; c != NULL && c->name != NULL;
2806229003Sglebius		    c++) {
2807229003Sglebius			if (binary->header.typecookie == c->cookie &&
2808229003Sglebius			    binary->header.cmd == c->cmd)
280953913Sarchie				break;
281053913Sarchie		}
281153913Sarchie		if (c == NULL || c->name == NULL) {
281253913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
2813229003Sglebius				if (binary->header.typecookie == c->cookie &&
2814229003Sglebius				    binary->header.cmd == c->cmd)
281553913Sarchie					break;
281653913Sarchie			}
281753913Sarchie			if (c->name == NULL) {
281870700Sjulian				NG_FREE_MSG(resp);
281953913Sarchie				error = ENOSYS;
282053913Sarchie				break;
282153913Sarchie			}
282253913Sarchie		}
282353913Sarchie
282453913Sarchie		/* Convert command name to ASCII */
282553913Sarchie		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
282653913Sarchie		    "%s", c->name);
282753913Sarchie
282853913Sarchie		/* Convert command arguments to ASCII */
282953913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
283053913Sarchie		    c->respType : c->mesgType;
283170912Sjulian		if (argstype == NULL) {
283253913Sarchie			*ascii->data = '\0';
283370912Sjulian		} else {
2834369764Sdonner			error = ng_unparse(argstype, (u_char *)binary->data,
2835369764Sdonner			    ascii->data, bufSize);
2836369764Sdonner			if (error == ERANGE) {
283770700Sjulian				NG_FREE_MSG(resp);
2838369764Sdonner				bufSize *= 2;
2839369764Sdonner				goto retry_b2a;
2840369764Sdonner			} else if (error) {
2841369764Sdonner				NG_FREE_MSG(resp);
284253913Sarchie				break;
284353913Sarchie			}
284453913Sarchie		}
284553913Sarchie
284653913Sarchie		/* Return the result as struct ng_mesg plus ASCII string */
284753913Sarchie		bufSize = strlen(ascii->data) + 1;
284853913Sarchie		ascii->header.arglen = bufSize;
284970700Sjulian		resp->header.arglen = sizeof(*ascii) + bufSize;
285053913Sarchie		break;
285153913Sarchie	    }
285253913Sarchie
285353913Sarchie	case NGM_ASCII2BINARY:
285453913Sarchie	    {
2855208036Szec		int bufSize = 20 * 1024;	/* XXX hard coded constant */
285653913Sarchie		const struct ng_cmdlist *c;
285753913Sarchie		const struct ng_parse_type *argstype;
285870700Sjulian		struct ng_mesg *ascii, *binary;
285959178Sarchie		int off = 0;
286053913Sarchie
286153913Sarchie		/* Data area must contain at least a struct ng_mesg + '\0' */
286253913Sarchie		ascii = (struct ng_mesg *)msg->data;
2863152451Sglebius		if ((msg->header.arglen < sizeof(*ascii) + 1) ||
2864152451Sglebius		    (ascii->header.arglen < 1) ||
2865152451Sglebius		    (msg->header.arglen < sizeof(*ascii) +
2866152451Sglebius		    ascii->header.arglen)) {
286771047Sjulian			TRAP_ERROR();
286853913Sarchie			error = EINVAL;
286953913Sarchie			break;
287053913Sarchie		}
287153913Sarchie		ascii->data[ascii->header.arglen - 1] = '\0';
287253913Sarchie
287370700Sjulian		/* Get a response message with lots of room */
287470700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
287570159Sjulian		if (resp == NULL) {
287653913Sarchie			error = ENOMEM;
287753913Sarchie			break;
287853913Sarchie		}
287970700Sjulian		binary = (struct ng_mesg *)resp->data;
288053913Sarchie
288153913Sarchie		/* Copy ASCII message header to response message payload */
288253913Sarchie		bcopy(ascii, binary, sizeof(*ascii));
288353913Sarchie
288453913Sarchie		/* Find command by matching ASCII command string */
288570784Sjulian		for (c = here->nd_type->cmdlist;
288653913Sarchie		    c != NULL && c->name != NULL; c++) {
288753913Sarchie			if (strcmp(ascii->header.cmdstr, c->name) == 0)
288853913Sarchie				break;
288953913Sarchie		}
289053913Sarchie		if (c == NULL || c->name == NULL) {
289153913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
289253913Sarchie				if (strcmp(ascii->header.cmdstr, c->name) == 0)
289353913Sarchie					break;
289453913Sarchie			}
289553913Sarchie			if (c->name == NULL) {
289670700Sjulian				NG_FREE_MSG(resp);
289753913Sarchie				error = ENOSYS;
289853913Sarchie				break;
289953913Sarchie			}
290053913Sarchie		}
290153913Sarchie
290253913Sarchie		/* Convert command name to binary */
290353913Sarchie		binary->header.cmd = c->cmd;
290453913Sarchie		binary->header.typecookie = c->cookie;
290553913Sarchie
290653913Sarchie		/* Convert command arguments to binary */
290753913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
290853913Sarchie		    c->respType : c->mesgType;
290970912Sjulian		if (argstype == NULL) {
291053913Sarchie			bufSize = 0;
291170912Sjulian		} else {
2912229003Sglebius			if ((error = ng_parse(argstype, ascii->data, &off,
2913229003Sglebius			    (u_char *)binary->data, &bufSize)) != 0) {
291470700Sjulian				NG_FREE_MSG(resp);
291553913Sarchie				break;
291653913Sarchie			}
291753913Sarchie		}
291853913Sarchie
291953913Sarchie		/* Return the result */
292053913Sarchie		binary->header.arglen = bufSize;
292170700Sjulian		resp->header.arglen = sizeof(*binary) + bufSize;
292253913Sarchie		break;
292353913Sarchie	    }
292453913Sarchie
292562471Sphk	case NGM_TEXT_CONFIG:
292652419Sjulian	case NGM_TEXT_STATUS:
292752419Sjulian		/*
292852419Sjulian		 * This one is tricky as it passes the command down to the
292952419Sjulian		 * actual node, even though it is a generic type command.
293070700Sjulian		 * This means we must assume that the item/msg is already freed
293152419Sjulian		 * when control passes back to us.
293252419Sjulian		 */
293370784Sjulian		if (here->nd_type->rcvmsg != NULL) {
293470700Sjulian			NGI_MSG(item) = msg; /* put it back as we found it */
293570784Sjulian			return((*here->nd_type->rcvmsg)(here, item, lasthook));
293652419Sjulian		}
293752419Sjulian		/* Fall through if rcvmsg not supported */
293852419Sjulian	default:
293971047Sjulian		TRAP_ERROR();
294052419Sjulian		error = EINVAL;
294152419Sjulian	}
294270700Sjulian	/*
294370700Sjulian	 * Sometimes a generic message may be statically allocated
2944229003Sglebius	 * to avoid problems with allocating when in tight memory situations.
294570700Sjulian	 * Don't free it if it is so.
2946298813Spfg	 * I break them apart here, because erros may cause a free if the item
294770700Sjulian	 * in which case we'd be doing it twice.
294870700Sjulian	 * they are kept together above, to simplify freeing.
294970700Sjulian	 */
295070700Sjulianout:
295170700Sjulian	NG_RESPOND_MSG(error, here, item, resp);
2952185179Smav	NG_FREE_MSG(msg);
295352419Sjulian	return (error);
295452419Sjulian}
295552419Sjulian
295652419Sjulian/************************************************************************
2957146213Sglebius			Queue element get/free routines
2958146213Sglebius************************************************************************/
2959146213Sglebius
2960146213Sglebiusuma_zone_t			ng_qzone;
2961178259Smavuma_zone_t			ng_qdzone;
2962186093Smavstatic int			numthreads = 0; /* number of queue threads */
2963176849Smavstatic int			maxalloc = 4096;/* limit the damage of a leak */
2964278640Sglebiusstatic int			maxdata = 4096;	/* limit the damage of a DoS */
2965146213Sglebius
2966186093SmavSYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads,
2967186093Smav    0, "Number of queue processing threads");
2968146213SglebiusSYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
2969178259Smav    0, "Maximum number of non-data queue items to allocate");
2970178259SmavSYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata,
2971178259Smav    0, "Maximum number of data queue items to allocate");
2972146213Sglebius
2973146213Sglebius#ifdef	NETGRAPH_DEBUG
2974146213Sglebiusstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
2975229003Sglebiusstatic int allocated;	/* number of items malloc'd */
2976146213Sglebius#endif
2977146213Sglebius
2978146213Sglebius/*
2979146213Sglebius * Get a queue entry.
2980146213Sglebius * This is usually called when a packet first enters netgraph.
2981146213Sglebius * By definition, this is usually from an interrupt, or from a user.
2982146213Sglebius * Users are not so important, but try be quick for the times that it's
2983146213Sglebius * an interrupt.
2984146213Sglebius */
2985146213Sglebiusstatic __inline item_p
2986178259Smavng_alloc_item(int type, int flags)
2987146213Sglebius{
2988178259Smav	item_p item;
2989146213Sglebius
2990178259Smav	KASSERT(((type & ~NGQF_TYPE) == 0),
2991178259Smav	    ("%s: incorrect item type: %d", __func__, type));
2992146213Sglebius
2993229003Sglebius	item = uma_zalloc((type == NGQF_DATA) ? ng_qdzone : ng_qzone,
2994178259Smav	    ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO);
2995146281Sglebius
2996178259Smav	if (item) {
2997178259Smav		item->el_flags = type;
2998146213Sglebius#ifdef	NETGRAPH_DEBUG
2999178259Smav		mtx_lock(&ngq_mtx);
3000178259Smav		TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
3001178259Smav		allocated++;
3002178259Smav		mtx_unlock(&ngq_mtx);
3003178259Smav#endif
3004146213Sglebius	}
3005146213Sglebius
3006146213Sglebius	return (item);
3007146213Sglebius}
3008146213Sglebius
3009146213Sglebius/*
3010146213Sglebius * Release a queue entry
3011146213Sglebius */
3012146213Sglebiusvoid
3013146213Sglebiusng_free_item(item_p item)
3014146213Sglebius{
3015146213Sglebius	/*
3016146213Sglebius	 * The item may hold resources on it's own. We need to free
3017146213Sglebius	 * these before we can free the item. What they are depends upon
3018146213Sglebius	 * what kind of item it is. it is important that nodes zero
3019146213Sglebius	 * out pointers to resources that they remove from the item
3020146213Sglebius	 * or we release them again here.
3021146213Sglebius	 */
3022146213Sglebius	switch (item->el_flags & NGQF_TYPE) {
3023146213Sglebius	case NGQF_DATA:
3024146213Sglebius		/* If we have an mbuf still attached.. */
3025146213Sglebius		NG_FREE_M(_NGI_M(item));
3026146213Sglebius		break;
3027146213Sglebius	case NGQF_MESG:
3028146213Sglebius		_NGI_RETADDR(item) = 0;
3029146213Sglebius		NG_FREE_MSG(_NGI_MSG(item));
3030146213Sglebius		break;
3031146213Sglebius	case NGQF_FN:
3032172806Smav	case NGQF_FN2:
3033146213Sglebius		/* nothing to free really, */
3034146213Sglebius		_NGI_FN(item) = NULL;
3035146213Sglebius		_NGI_ARG1(item) = NULL;
3036146213Sglebius		_NGI_ARG2(item) = 0;
3037146213Sglebius		break;
3038146213Sglebius	}
3039146213Sglebius	/* If we still have a node or hook referenced... */
3040146213Sglebius	_NGI_CLR_NODE(item);
3041146213Sglebius	_NGI_CLR_HOOK(item);
3042146213Sglebius
3043146213Sglebius#ifdef	NETGRAPH_DEBUG
3044146213Sglebius	mtx_lock(&ngq_mtx);
3045146213Sglebius	TAILQ_REMOVE(&ng_itemlist, item, all);
3046146213Sglebius	allocated--;
3047146213Sglebius	mtx_unlock(&ngq_mtx);
3048146213Sglebius#endif
3049229003Sglebius	uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA) ?
3050229003Sglebius	    ng_qdzone : ng_qzone, item);
3051146213Sglebius}
3052146213Sglebius
3053178259Smav/*
3054178259Smav * Change type of the queue entry.
3055178259Smav * Possibly reallocates it from another UMA zone.
3056178259Smav */
3057178259Smavstatic __inline item_p
3058178259Smavng_realloc_item(item_p pitem, int type, int flags)
3059178259Smav{
3060178259Smav	item_p item;
3061178259Smav	int from, to;
3062178259Smav
3063178259Smav	KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__));
3064178259Smav	KASSERT(((type & ~NGQF_TYPE) == 0),
3065178259Smav	    ("%s: incorrect item type: %d", __func__, type));
3066178259Smav
3067178259Smav	from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA);
3068178259Smav	to = (type == NGQF_DATA);
3069178259Smav	if (from != to) {
3070178259Smav		/* If reallocation is required do it and copy item. */
3071178259Smav		if ((item = ng_alloc_item(type, flags)) == NULL) {
3072178259Smav			ng_free_item(pitem);
3073178259Smav			return (NULL);
3074178259Smav		}
3075178259Smav		*item = *pitem;
3076178259Smav		ng_free_item(pitem);
3077178259Smav	} else
3078178259Smav		item = pitem;
3079178259Smav	item->el_flags = (item->el_flags & ~NGQF_TYPE) | type;
3080178259Smav
3081178259Smav	return (item);
3082178259Smav}
3083178259Smav
3084146213Sglebius/************************************************************************
308552419Sjulian			Module routines
308652419Sjulian************************************************************************/
308752419Sjulian
308852419Sjulian/*
308952419Sjulian * Handle the loading/unloading of a netgraph node type module
309052419Sjulian */
309152419Sjulianint
309252419Sjulianng_mod_event(module_t mod, int event, void *data)
309352419Sjulian{
309452419Sjulian	struct ng_type *const type = data;
3095229003Sglebius	int error = 0;
309652419Sjulian
309752419Sjulian	switch (event) {
309852419Sjulian	case MOD_LOAD:
309952419Sjulian
310052419Sjulian		/* Register new netgraph node type */
3101229003Sglebius		if ((error = ng_newtype(type)) != 0)
310252419Sjulian			break;
310352419Sjulian
310452419Sjulian		/* Call type specific code */
310552419Sjulian		if (type->mod_event != NULL)
310670700Sjulian			if ((error = (*type->mod_event)(mod, event, data))) {
3107230480Sglebius				TYPELIST_WLOCK();
310871603Sjulian				type->refs--;	/* undo it */
310952419Sjulian				LIST_REMOVE(type, types);
3110230480Sglebius				TYPELIST_WUNLOCK();
311170700Sjulian			}
311252419Sjulian		break;
311352419Sjulian
311452419Sjulian	case MOD_UNLOAD:
311571603Sjulian		if (type->refs > 1) {		/* make sure no nodes exist! */
311652419Sjulian			error = EBUSY;
311771603Sjulian		} else {
3118229003Sglebius			if (type->refs == 0) /* failed load, nothing to undo */
311971603Sjulian				break;
312052419Sjulian			if (type->mod_event != NULL) {	/* check with type */
312152419Sjulian				error = (*type->mod_event)(mod, event, data);
3122229003Sglebius				if (error != 0)	/* type refuses.. */
312352419Sjulian					break;
312452419Sjulian			}
3125230480Sglebius			TYPELIST_WLOCK();
312652419Sjulian			LIST_REMOVE(type, types);
3127230480Sglebius			TYPELIST_WUNLOCK();
312852419Sjulian		}
312952419Sjulian		break;
313052419Sjulian
313152419Sjulian	default:
313252419Sjulian		if (type->mod_event != NULL)
313352419Sjulian			error = (*type->mod_event)(mod, event, data);
313452419Sjulian		else
3135132199Sphk			error = EOPNOTSUPP;		/* XXX ? */
313652419Sjulian		break;
313752419Sjulian	}
313852419Sjulian	return (error);
313952419Sjulian}
314052419Sjulian
3141231831Sglebiusstatic void
3142231831Sglebiusvnet_netgraph_init(const void *unused __unused)
3143231831Sglebius{
3144231831Sglebius
3145231831Sglebius	/* We start with small hashes, but they can grow. */
3146231831Sglebius	V_ng_ID_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_ID_hmask);
3147231831Sglebius	V_ng_name_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_name_hmask);
3148231831Sglebius}
3149231831SglebiusVNET_SYSINIT(vnet_netgraph_init, SI_SUB_NETGRAPH, SI_ORDER_FIRST,
3150231831Sglebius    vnet_netgraph_init, NULL);
3151231831Sglebius
3152229003Sglebius#ifdef VIMAGE
3153195837Srwatsonstatic void
3154195837Srwatsonvnet_netgraph_uninit(const void *unused __unused)
3155193731Szec{
3156207572Szec	node_p node = NULL, last_killed = NULL;
3157207572Szec	int i;
3158193731Szec
3159207572Szec	do {
3160207572Szec		/* Find a node to kill */
3161231831Sglebius		IDHASH_RLOCK();
3162231831Sglebius		for (i = 0; i <= V_ng_ID_hmask; i++) {
3163231831Sglebius			LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) {
3164207572Szec				if (node != &ng_deadnode) {
3165207572Szec					NG_NODE_REF(node);
3166207572Szec					break;
3167207572Szec				}
3168207572Szec			}
3169207572Szec			if (node != NULL)
3170207572Szec				break;
3171207572Szec		}
3172231831Sglebius		IDHASH_RUNLOCK();
3173207572Szec
3174207572Szec		/* Attempt to kill it only if it is a regular node */
3175207572Szec		if (node != NULL) {
3176207572Szec			if (node == last_killed) {
3177207572Szec				/* This should never happen */
3178229003Sglebius				printf("ng node %s needs NGF_REALLY_DIE\n",
3179229003Sglebius				    node->nd_name);
3180207572Szec				if (node->nd_flags & NGF_REALLY_DIE)
3181207572Szec					panic("ng node %s won't die",
3182207572Szec					    node->nd_name);
3183207572Szec				node->nd_flags |= NGF_REALLY_DIE;
3184207572Szec			}
3185193731Szec			ng_rmnode(node, NULL, NULL, 0);
3186207572Szec			NG_NODE_UNREF(node);
3187207572Szec			last_killed = node;
3188193731Szec		}
3189207572Szec	} while (node != NULL);
3190231831Sglebius
3191231831Sglebius	hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask);
3192231831Sglebius	hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_ID_hmask);
3193193731Szec}
3194231830SglebiusVNET_SYSUNINIT(vnet_netgraph_uninit, SI_SUB_NETGRAPH, SI_ORDER_FIRST,
3195195837Srwatson    vnet_netgraph_uninit, NULL);
3196193731Szec#endif /* VIMAGE */
3197193731Szec
319852419Sjulian/*
319952419Sjulian * Handle loading and unloading for this code.
320052419Sjulian * The only thing we need to link into is the NETISR strucure.
320152419Sjulian */
320252419Sjulianstatic int
320352419Sjulianngb_mod_event(module_t mod, int event, void *data)
320452419Sjulian{
3205186093Smav	struct proc *p;
3206186093Smav	struct thread *td;
3207186093Smav	int i, error = 0;
320852419Sjulian
320952419Sjulian	switch (event) {
321052419Sjulian	case MOD_LOAD:
3211146212Sglebius		/* Initialize everything. */
3212168049Swkoszek		NG_WORKLIST_LOCK_INIT();
3213230480Sglebius		rw_init(&ng_typelist_lock, "netgraph types");
3214230480Sglebius		rw_init(&ng_idhash_lock, "netgraph idhash");
3215230480Sglebius		rw_init(&ng_namehash_lock, "netgraph namehash");
3216256550Smelifaro		rw_init(&ng_topo_lock, "netgraph topology mutex");
3217146212Sglebius#ifdef	NETGRAPH_DEBUG
3218176802Smav		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3219176802Smav		    MTX_DEF);
3220146212Sglebius		mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3221123278Struckman		    MTX_DEF);
3222146212Sglebius#endif
3223146212Sglebius		ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
3224231997Sglebius		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
3225146212Sglebius		uma_zone_set_max(ng_qzone, maxalloc);
3226229003Sglebius		ng_qdzone = uma_zcreate("NetGraph data items",
3227229003Sglebius		    sizeof(struct ng_item), NULL, NULL, NULL, NULL,
3228231997Sglebius		    UMA_ALIGN_CACHE, 0);
3229178259Smav		uma_zone_set_max(ng_qdzone, maxdata);
3230186093Smav		/* Autoconfigure number of threads. */
3231186093Smav		if (numthreads <= 0)
3232186093Smav			numthreads = mp_ncpus;
3233186093Smav		/* Create threads. */
3234186093Smav    		p = NULL; /* start with no process */
3235186093Smav		for (i = 0; i < numthreads; i++) {
3236186093Smav			if (kproc_kthread_add(ngthread, NULL, &p, &td,
3237186093Smav			    RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) {
3238186093Smav				numthreads = i;
3239186093Smav				break;
3240186093Smav			}
3241186093Smav		}
324252419Sjulian		break;
324352419Sjulian	case MOD_UNLOAD:
3244167677Srwatson		/* You can't unload it because an interface may be using it. */
324552419Sjulian		error = EBUSY;
324652419Sjulian		break;
324752419Sjulian	default:
324852419Sjulian		error = EOPNOTSUPP;
324952419Sjulian		break;
325052419Sjulian	}
325152419Sjulian	return (error);
325252419Sjulian}
325352419Sjulian
325452419Sjulianstatic moduledata_t netgraph_mod = {
325552419Sjulian	"netgraph",
325652419Sjulian	ngb_mod_event,
325752419Sjulian	(NULL)
325852419Sjulian};
3259231830SglebiusDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_FIRST);
326072946SjulianSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
3261273377ShselaskySYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_ABI_VERSION,"");
3262273377ShselaskySYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_VERSION, "");
326352419Sjulian
326470784Sjulian#ifdef	NETGRAPH_DEBUG
326570700Sjulianvoid
326670784Sjuliandumphook (hook_p hook, char *file, int line)
326770784Sjulian{
326870784Sjulian	printf("hook: name %s, %d refs, Last touched:\n",
326970784Sjulian		_NG_HOOK_NAME(hook), hook->hk_refs);
327070784Sjulian	printf("	Last active @ %s, line %d\n",
327170784Sjulian		hook->lastfile, hook->lastline);
327270784Sjulian	if (line) {
327370784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
3274226829Sglebius#ifdef KDB
3275226829Sglebius		kdb_backtrace();
3276226829Sglebius#endif
327770784Sjulian	}
327870784Sjulian}
327970784Sjulian
328070784Sjulianvoid
328170784Sjuliandumpnode(node_p node, char *file, int line)
328270784Sjulian{
328370784Sjulian	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
328471047Sjulian		_NG_NODE_ID(node), node->nd_type->name,
328570784Sjulian		node->nd_numhooks, node->nd_flags,
328670784Sjulian		node->nd_refs, node->nd_name);
328770784Sjulian	printf("	Last active @ %s, line %d\n",
328870784Sjulian		node->lastfile, node->lastline);
328970784Sjulian	if (line) {
329070784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
3291226829Sglebius#ifdef KDB
3292226829Sglebius		kdb_backtrace();
3293226829Sglebius#endif
329470784Sjulian	}
329570784Sjulian}
329670784Sjulian
329770784Sjulianvoid
329870700Sjuliandumpitem(item_p item, char *file, int line)
329970700Sjulian{
3300146212Sglebius	printf(" ACTIVE item, last used at %s, line %d",
3301146212Sglebius		item->lastfile, item->lastline);
3302146212Sglebius	switch(item->el_flags & NGQF_TYPE) {
3303146212Sglebius	case NGQF_DATA:
3304146212Sglebius		printf(" - [data]\n");
3305146212Sglebius		break;
3306146212Sglebius	case NGQF_MESG:
3307146212Sglebius		printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
3308146212Sglebius		break;
3309146212Sglebius	case NGQF_FN:
3310172820Sru		printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3311172820Sru			_NGI_FN(item),
3312172820Sru			_NGI_NODE(item),
3313172820Sru			_NGI_HOOK(item),
3314172820Sru			item->body.fn.fn_arg1,
3315172820Sru			item->body.fn.fn_arg2,
3316172820Sru			item->body.fn.fn_arg2);
3317172820Sru		break;
3318172806Smav	case NGQF_FN2:
3319173110Smav		printf(" - fn2@%p (%p, %p, %p, %d (%x))\n",
3320172820Sru			_NGI_FN2(item),
3321149735Sglebius			_NGI_NODE(item),
3322149735Sglebius			_NGI_HOOK(item),
3323146212Sglebius			item->body.fn.fn_arg1,
3324146212Sglebius			item->body.fn.fn_arg2,
3325146212Sglebius			item->body.fn.fn_arg2);
3326146212Sglebius		break;
332752419Sjulian	}
332870784Sjulian	if (line) {
332970784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
3330149735Sglebius		if (_NGI_NODE(item)) {
333170784Sjulian			printf("node %p ([%x])\n",
3332149735Sglebius				_NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
333370784Sjulian		}
333470784Sjulian	}
333570700Sjulian}
333652419Sjulian
333770784Sjulianstatic void
333870784Sjulianng_dumpitems(void)
333970784Sjulian{
334070784Sjulian	item_p item;
334170784Sjulian	int i = 1;
334270784Sjulian	TAILQ_FOREACH(item, &ng_itemlist, all) {
334370784Sjulian		printf("[%d] ", i++);
334470784Sjulian		dumpitem(item, NULL, 0);
334570784Sjulian	}
334670784Sjulian}
334770784Sjulian
334870784Sjulianstatic void
334970784Sjulianng_dumpnodes(void)
335070784Sjulian{
335170784Sjulian	node_p node;
335270784Sjulian	int i = 1;
3353131008Srwatson	mtx_lock(&ng_nodelist_mtx);
335470784Sjulian	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
335570784Sjulian		printf("[%d] ", i++);
335670784Sjulian		dumpnode(node, NULL, 0);
335770784Sjulian	}
3358131008Srwatson	mtx_unlock(&ng_nodelist_mtx);
335970784Sjulian}
336070784Sjulian
336170784Sjulianstatic void
336270784Sjulianng_dumphooks(void)
336370784Sjulian{
336470784Sjulian	hook_p hook;
336570784Sjulian	int i = 1;
3366131008Srwatson	mtx_lock(&ng_nodelist_mtx);
336770784Sjulian	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
336870784Sjulian		printf("[%d] ", i++);
336970784Sjulian		dumphook(hook, NULL, 0);
337070784Sjulian	}
3371131008Srwatson	mtx_unlock(&ng_nodelist_mtx);
337270784Sjulian}
337370784Sjulian
337470700Sjulianstatic int
337570700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
337670700Sjulian{
337770700Sjulian	int error;
337870700Sjulian	int val;
337970700Sjulian	int i;
338070700Sjulian
338170700Sjulian	val = allocated;
338270700Sjulian	i = 1;
3383170289Sdwmalone	error = sysctl_handle_int(oidp, &val, 0, req);
338471047Sjulian	if (error != 0 || req->newptr == NULL)
338571047Sjulian		return (error);
338671047Sjulian	if (val == 42) {
338770784Sjulian		ng_dumpitems();
338870784Sjulian		ng_dumpnodes();
338970784Sjulian		ng_dumphooks();
339070700Sjulian	}
339171047Sjulian	return (0);
339252419Sjulian}
339352419Sjulian
339471047SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
339571047Sjulian    0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
339670784Sjulian#endif	/* NETGRAPH_DEBUG */
339770700Sjulian
339870700Sjulian/***********************************************************************
339970700Sjulian* Worklist routines
340070700Sjulian**********************************************************************/
340152419Sjulian/*
340270700Sjulian * Pick a node off the list of nodes with work,
3403186093Smav * try get an item to process off it. Remove the node from the list.
340452419Sjulian */
340570700Sjulianstatic void
3406186093Smavngthread(void *arg)
340752419Sjulian{
3408177953Smav	for (;;) {
3409177953Smav		node_p  node;
341052419Sjulian
3411177953Smav		/* Get node from the worklist. */
3412168049Swkoszek		NG_WORKLIST_LOCK();
3413186093Smav		while ((node = STAILQ_FIRST(&ng_worklist)) == NULL)
3414186093Smav			NG_WORKLIST_SLEEP();
3415178228Smav		STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work);
3416168049Swkoszek		NG_WORKLIST_UNLOCK();
3417193731Szec		CURVNET_SET(node->nd_vnet);
3418154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
3419154275Sglebius		    __func__, node->nd_ID, node);
342070700Sjulian		/*
342170700Sjulian		 * We have the node. We also take over the reference
342270700Sjulian		 * that the list had on it.
342370700Sjulian		 * Now process as much as you can, until it won't
342470700Sjulian		 * let you have another item off the queue.
342570700Sjulian		 * All this time, keep the reference
342670700Sjulian		 * that lets us be sure that the node still exists.
342770700Sjulian		 * Let the reference go at the last minute.
342870700Sjulian		 */
342970700Sjulian		for (;;) {
3430177953Smav			item_p item;
3431151238Sglebius			int rw;
3432151238Sglebius
3433168049Swkoszek			NG_QUEUE_LOCK(&node->nd_input_queue);
3434178228Smav			item = ng_dequeue(node, &rw);
343570700Sjulian			if (item == NULL) {
3436178228Smav				node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ;
3437168049Swkoszek				NG_QUEUE_UNLOCK(&node->nd_input_queue);
343870700Sjulian				break; /* go look for another node */
343970700Sjulian			} else {
3440168049Swkoszek				NG_QUEUE_UNLOCK(&node->nd_input_queue);
344174078Sjulian				NGI_GET_NODE(item, node); /* zaps stored node */
3442151238Sglebius				ng_apply_item(node, item, rw);
344374078Sjulian				NG_NODE_UNREF(node);
344470700Sjulian			}
344570700Sjulian		}
344673238Sjulian		NG_NODE_UNREF(node);
3447193731Szec		CURVNET_RESTORE();
344852419Sjulian	}
344970700Sjulian}
345069922Sjulian
345172979Sjulian/*
345272979Sjulian * XXX
345372979Sjulian * It's posible that a debugging NG_NODE_REF may need
345472979Sjulian * to be outside the mutex zone
345572979Sjulian */
345670700Sjulianstatic void
3457177953Smavng_worklist_add(node_p node)
345870700Sjulian{
3459148236Sglebius
3460148266Sglebius	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3461148236Sglebius
3462178228Smav	if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) {
346369922Sjulian		/*
346470700Sjulian		 * If we are not already on the work queue,
346570700Sjulian		 * then put us on.
346669922Sjulian		 */
3467178228Smav		node->nd_input_queue.q_flags2 |= NGQ2_WORKQ;
3468229003Sglebius		NG_NODE_REF(node); /* XXX safe in mutex? */
3469168049Swkoszek		NG_WORKLIST_LOCK();
3470178228Smav		STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work);
3471168049Swkoszek		NG_WORKLIST_UNLOCK();
3472154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
3473154275Sglebius		    node->nd_ID, node);
3474186093Smav		NG_WORKLIST_WAKEUP();
3475177953Smav	} else {
3476154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
3477154275Sglebius		    __func__, node->nd_ID, node);
3478177953Smav	}
347970700Sjulian}
348070700Sjulian
348170700Sjulian/***********************************************************************
348270700Sjulian* Externally useable functions to set up a queue item ready for sending
348370700Sjulian***********************************************************************/
348470700Sjulian
348570784Sjulian#ifdef	NETGRAPH_DEBUG
348670784Sjulian#define	ITEM_DEBUG_CHECKS						\
348770700Sjulian	do {								\
348871849Sjulian		if (NGI_NODE(item) ) {					\
348970700Sjulian			printf("item already has node");		\
3490174898Srwatson			kdb_enter(KDB_WHY_NETGRAPH, "has node");	\
349171849Sjulian			NGI_CLR_NODE(item);				\
349270700Sjulian		}							\
349371849Sjulian		if (NGI_HOOK(item) ) {					\
349470700Sjulian			printf("item already has hook");		\
3495174898Srwatson			kdb_enter(KDB_WHY_NETGRAPH, "has hook");	\
349671849Sjulian			NGI_CLR_HOOK(item);				\
349770700Sjulian		}							\
349870700Sjulian	} while (0)
349970700Sjulian#else
350070784Sjulian#define ITEM_DEBUG_CHECKS
350170700Sjulian#endif
350270700Sjulian
350370700Sjulian/*
3504131374Sjulian * Put mbuf into the item.
350570700Sjulian * Hook and node references will be removed when the item is dequeued.
350670700Sjulian * (or equivalent)
350770700Sjulian * (XXX) Unsafe because no reference held by peer on remote node.
350870700Sjulian * remote node might go away in this timescale.
350970700Sjulian * We know the hooks can't go away because that would require getting
351070700Sjulian * a writer item on both nodes and we must have at least a  reader
3511151973Sglebius * here to be able to do this.
351270700Sjulian * Note that the hook loaded is the REMOTE hook.
351370700Sjulian *
351470700Sjulian * This is possibly in the critical path for new data.
351570700Sjulian */
351670700Sjulianitem_p
3517146281Sglebiusng_package_data(struct mbuf *m, int flags)
351870700Sjulian{
351970700Sjulian	item_p item;
352070700Sjulian
3521178259Smav	if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) {
3522176849Smav		NG_FREE_M(m);
3523176849Smav		return (NULL);
3524176849Smav	}
352570784Sjulian	ITEM_DEBUG_CHECKS;
3526178259Smav	item->el_flags |= NGQF_READER;
352770700Sjulian	NGI_M(item) = m;
352870700Sjulian	return (item);
352970700Sjulian}
353070700Sjulian
353170700Sjulian/*
353270700Sjulian * Allocate a queue item and put items into it..
353370700Sjulian * Evaluate the address as this will be needed to queue it and
353470700Sjulian * to work out what some of the fields should be.
353570700Sjulian * Hook and node references will be removed when the item is dequeued.
353670700Sjulian * (or equivalent)
353770700Sjulian */
353870700Sjulianitem_p
3539146281Sglebiusng_package_msg(struct ng_mesg *msg, int flags)
354070700Sjulian{
354170700Sjulian	item_p item;
354270700Sjulian
3543178259Smav	if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) {
354471849Sjulian		NG_FREE_MSG(msg);
354570700Sjulian		return (NULL);
354669922Sjulian	}
354770784Sjulian	ITEM_DEBUG_CHECKS;
3548149505Sglebius	/* Messages items count as writers unless explicitly exempted. */
3549149505Sglebius	if (msg->header.cmd & NGM_READONLY)
3550178259Smav		item->el_flags |= NGQF_READER;
3551149505Sglebius	else
3552178259Smav		item->el_flags |= NGQF_WRITER;
355370700Sjulian	/*
355470700Sjulian	 * Set the current lasthook into the queue item
355570700Sjulian	 */
355670700Sjulian	NGI_MSG(item) = msg;
3557102244Sarchie	NGI_RETADDR(item) = 0;
355870700Sjulian	return (item);
355970700Sjulian}
356069922Sjulian
356171849Sjulian#define SET_RETADDR(item, here, retaddr)				\
356271047Sjulian	do {	/* Data or fn items don't have retaddrs */		\
356371047Sjulian		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
356470700Sjulian			if (retaddr) {					\
356570700Sjulian				NGI_RETADDR(item) = retaddr;		\
356670700Sjulian			} else {					\
356770700Sjulian				/*					\
356870700Sjulian				 * The old return address should be ok.	\
356970700Sjulian				 * If there isn't one, use the address	\
357070700Sjulian				 * here.				\
357170700Sjulian				 */					\
357270700Sjulian				if (NGI_RETADDR(item) == 0) {		\
357370700Sjulian					NGI_RETADDR(item)		\
357470700Sjulian						= ng_node2ID(here);	\
357570700Sjulian				}					\
357670700Sjulian			}						\
357770700Sjulian		}							\
357870700Sjulian	} while (0)
357970700Sjulian
358070700Sjulianint
358170700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
358270700Sjulian{
358371849Sjulian	hook_p peer;
358471849Sjulian	node_p peernode;
358570784Sjulian	ITEM_DEBUG_CHECKS;
358670700Sjulian	/*
358770700Sjulian	 * Quick sanity check..
358870784Sjulian	 * Since a hook holds a reference on it's node, once we know
358970784Sjulian	 * that the peer is still connected (even if invalid,) we know
359070784Sjulian	 * that the peer node is present, though maybe invalid.
359170700Sjulian	 */
3592256550Smelifaro	TOPOLOGY_RLOCK();
3593229003Sglebius	if ((hook == NULL) || NG_HOOK_NOT_VALID(hook) ||
3594178311Smav	    NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) ||
3595178311Smav	    NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) {
359670700Sjulian		NG_FREE_ITEM(item);
359771047Sjulian		TRAP_ERROR();
3598256550Smelifaro		TOPOLOGY_RUNLOCK();
359973083Sjulian		return (ENETDOWN);
360052419Sjulian	}
360152419Sjulian
360270700Sjulian	/*
360370700Sjulian	 * Transfer our interest to the other (peer) end.
360470700Sjulian	 */
360571849Sjulian	NG_HOOK_REF(peer);
3606178311Smav	NG_NODE_REF(peernode);
360771849Sjulian	NGI_SET_HOOK(item, peer);
360871849Sjulian	NGI_SET_NODE(item, peernode);
360979706Sjulian	SET_RETADDR(item, here, retaddr);
3610219827Sglebius
3611256550Smelifaro	TOPOLOGY_RUNLOCK();
3612219827Sglebius
361370700Sjulian	return (0);
361470700Sjulian}
361552419Sjulian
361670700Sjulianint
3617227130Sfjoeng_address_path(node_p here, item_p item, const char *address, ng_ID_t retaddr)
361870700Sjulian{
3619152451Sglebius	node_p	dest = NULL;
362070700Sjulian	hook_p	hook = NULL;
3621152451Sglebius	int	error;
362270700Sjulian
362370784Sjulian	ITEM_DEBUG_CHECKS;
362470700Sjulian	/*
362570700Sjulian	 * Note that ng_path2noderef increments the reference count
362670700Sjulian	 * on the node for us if it finds one. So we don't have to.
362770700Sjulian	 */
362870700Sjulian	error = ng_path2noderef(here, address, &dest, &hook);
362970700Sjulian	if (error) {
363070700Sjulian		NG_FREE_ITEM(item);
363170784Sjulian		return (error);
363252419Sjulian	}
363371849Sjulian	NGI_SET_NODE(item, dest);
3634219827Sglebius	if (hook)
363571849Sjulian		NGI_SET_HOOK(item, hook);
3636219827Sglebius
363771849Sjulian	SET_RETADDR(item, here, retaddr);
363870700Sjulian	return (0);
363970700Sjulian}
364052419Sjulian
364170700Sjulianint
364270700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
364370700Sjulian{
364470700Sjulian	node_p dest;
364570700Sjulian
364670784Sjulian	ITEM_DEBUG_CHECKS;
364770700Sjulian	/*
364870700Sjulian	 * Find the target node.
364970700Sjulian	 */
365070700Sjulian	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
365170700Sjulian	if (dest == NULL) {
365270700Sjulian		NG_FREE_ITEM(item);
365371047Sjulian		TRAP_ERROR();
365470700Sjulian		return(EINVAL);
365570700Sjulian	}
365670700Sjulian	/* Fill out the contents */
365771849Sjulian	NGI_SET_NODE(item, dest);
365871849Sjulian	NGI_CLR_HOOK(item);
365971849Sjulian	SET_RETADDR(item, here, retaddr);
366052419Sjulian	return (0);
366152419Sjulian}
366252419Sjulian
366352419Sjulian/*
366470700Sjulian * special case to send a message to self (e.g. destroy node)
366570700Sjulian * Possibly indicate an arrival hook too.
366670700Sjulian * Useful for removing that hook :-)
366752419Sjulian */
366870700Sjulianitem_p
366970700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
367052419Sjulian{
367170700Sjulian	item_p item;
367252419Sjulian
367370700Sjulian	/*
367470700Sjulian	 * Find the target node.
367570700Sjulian	 * If there is a HOOK argument, then use that in preference
367670700Sjulian	 * to the address.
367770700Sjulian	 */
3678178259Smav	if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) {
367971849Sjulian		NG_FREE_MSG(msg);
368070700Sjulian		return (NULL);
368152419Sjulian	}
368270700Sjulian
368370700Sjulian	/* Fill out the contents */
3684178259Smav	item->el_flags |= NGQF_WRITER;
368570784Sjulian	NG_NODE_REF(here);
368671849Sjulian	NGI_SET_NODE(item, here);
368771849Sjulian	if (hook) {
368870784Sjulian		NG_HOOK_REF(hook);
368971849Sjulian		NGI_SET_HOOK(item, hook);
369071849Sjulian	}
369170700Sjulian	NGI_MSG(item) = msg;
369270700Sjulian	NGI_RETADDR(item) = ng_node2ID(here);
369370700Sjulian	return (item);
369452419Sjulian}
369552419Sjulian
3696172806Smav/*
3697172806Smav * Send ng_item_fn function call to the specified node.
3698172806Smav */
3699172806Smav
3700146281Sglebiusint
3701173605Sglebiusng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2)
370271047Sjulian{
370371047Sjulian
3704173605Sglebius	return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS);
370571047Sjulian}
370671047Sjulian
3707172806Smavint
3708173605Sglebiusng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
3709172806Smav	int flags)
3710172806Smav{
3711172806Smav	item_p item;
3712172806Smav
3713178259Smav	if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) {
3714172806Smav		return (ENOMEM);
3715172806Smav	}
3716178259Smav	item->el_flags |= NGQF_WRITER;
3717172806Smav	NG_NODE_REF(node); /* and one for the item */
3718172806Smav	NGI_SET_NODE(item, node);
3719172806Smav	if (hook) {
3720172806Smav		NG_HOOK_REF(hook);
3721172806Smav		NGI_SET_HOOK(item, hook);
3722172806Smav	}
3723173605Sglebius	NGI_FN(item) = fn;
3724172806Smav	NGI_ARG1(item) = arg1;
3725172806Smav	NGI_ARG2(item) = arg2;
3726172806Smav	return(ng_snd_item(item, flags));
3727172806Smav}
3728172806Smav
3729172806Smav/*
3730173605Sglebius * Send ng_item_fn2 function call to the specified node.
3731173605Sglebius *
3732173605Sglebius * If an optional pitem parameter is supplied, its apply
3733173605Sglebius * callback will be copied to the new item. If also NG_REUSE_ITEM
3734173605Sglebius * flag is set, no new item will be allocated, but pitem will
3735173605Sglebius * be used.
3736172806Smav */
3737172806Smavint
3738173605Sglebiusng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1,
3739173605Sglebius	int arg2, int flags)
3740172806Smav{
3741172806Smav	item_p item;
3742172806Smav
3743173605Sglebius	KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0),
3744173605Sglebius	    ("%s: NG_REUSE_ITEM but no pitem", __func__));
3745172806Smav
3746173605Sglebius	/*
3747173605Sglebius	 * Allocate a new item if no supplied or
3748173605Sglebius	 * if we can't use supplied one.
3749173605Sglebius	 */
3750173605Sglebius	if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) {
3751178259Smav		if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL)
3752173605Sglebius			return (ENOMEM);
3753178259Smav		if (pitem != NULL)
3754178259Smav			item->apply = pitem->apply;
3755176849Smav	} else {
3756178259Smav		if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL)
3757178259Smav			return (ENOMEM);
3758176849Smav	}
3759172806Smav
3760178259Smav	item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER;
3761172806Smav	NG_NODE_REF(node); /* and one for the item */
3762172806Smav	NGI_SET_NODE(item, node);
3763172806Smav	if (hook) {
3764172806Smav		NG_HOOK_REF(hook);
3765172806Smav		NGI_SET_HOOK(item, hook);
3766172806Smav	}
3767172806Smav	NGI_FN2(item) = fn;
3768172806Smav	NGI_ARG1(item) = arg1;
3769172806Smav	NGI_ARG2(item) = arg2;
3770172806Smav	return(ng_snd_item(item, flags));
3771172806Smav}
3772172806Smav
3773172806Smav/*
377491711Sjulian * Official timeout routines for Netgraph nodes.
377591711Sjulian */
377691711Sjulianstatic void
3777140852Sglebiusng_callout_trampoline(void *arg)
377891711Sjulian{
377991711Sjulian	item_p item = arg;
378091711Sjulian
3781193731Szec	CURVNET_SET(NGI_NODE(item)->nd_vnet);
378291711Sjulian	ng_snd_item(item, 0);
3783193731Szec	CURVNET_RESTORE();
378491711Sjulian}
378591711Sjulian
3786137138Sglebiusint
3787138268Sglebiusng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
378891711Sjulian    ng_item_fn *fn, void * arg1, int arg2)
378991711Sjulian{
3790149881Sglebius	item_p item, oitem;
379191711Sjulian
3792178259Smav	if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL)
3793137138Sglebius		return (ENOMEM);
3794137138Sglebius
3795178259Smav	item->el_flags |= NGQF_WRITER;
379691711Sjulian	NG_NODE_REF(node);		/* and one for the item */
379791711Sjulian	NGI_SET_NODE(item, node);
379891711Sjulian	if (hook) {
379991711Sjulian		NG_HOOK_REF(hook);
380091711Sjulian		NGI_SET_HOOK(item, hook);
380191711Sjulian	}
380291711Sjulian	NGI_FN(item) = fn;
380391711Sjulian	NGI_ARG1(item) = arg1;
380491711Sjulian	NGI_ARG2(item) = arg2;
3805149881Sglebius	oitem = c->c_arg;
3806149881Sglebius	if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
3807149881Sglebius	    oitem != NULL)
3808149881Sglebius		NG_FREE_ITEM(oitem);
3809137138Sglebius	return (0);
381091711Sjulian}
381191711Sjulian
381291711Sjulian/* A special modified version of untimeout() */
3813152451Sglebiusint
3814138268Sglebiusng_uncallout(struct callout *c, node_p node)
381591711Sjulian{
381691711Sjulian	item_p item;
3817137138Sglebius	int rval;
3818149357Sglebius
3819149357Sglebius	KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
3820149357Sglebius	KASSERT(node != NULL, ("ng_uncallout: NULL node"));
3821149357Sglebius
3822137230Sglebius	rval = callout_stop(c);
3823137138Sglebius	item = c->c_arg;
3824137138Sglebius	/* Do an extra check */
3825140852Sglebius	if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
3826333615Ssbruno	    (item != NULL) && (NGI_NODE(item) == node)) {
382791711Sjulian		/*
382891711Sjulian		 * We successfully removed it from the queue before it ran
3829152451Sglebius		 * So now we need to unreference everything that was
383091711Sjulian		 * given extra references. (NG_FREE_ITEM does this).
383191711Sjulian		 */
383291711Sjulian		NG_FREE_ITEM(item);
383391711Sjulian	}
3834149881Sglebius	c->c_arg = NULL;
3835137138Sglebius
3836310248Shselasky	/*
3837310248Shselasky	 * Callers only want to know if the callout was cancelled and
3838310248Shselasky	 * not draining or stopped.
3839310248Shselasky	 */
3840310248Shselasky	return (rval > 0);
384191711Sjulian}
384291711Sjulian
384370700Sjulian/*
384470700Sjulian * Set the address, if none given, give the node here.
384570700Sjulian */
384670700Sjulianvoid
384770700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
384870700Sjulian{
384970700Sjulian	if (retaddr) {
385070700Sjulian		NGI_RETADDR(item) = retaddr;
385170700Sjulian	} else {
385270700Sjulian		/*
385370700Sjulian		 * The old return address should be ok.
385470700Sjulian		 * If there isn't one, use the address here.
385570700Sjulian		 */
385670700Sjulian		NGI_RETADDR(item) = ng_node2ID(here);
385770700Sjulian	}
385870700Sjulian}
3859