ng_base.c revision 167385
152419Sjulian/*
252419Sjulian * ng_base.c
3139823Simp */
4139823Simp
5139823Simp/*-
652419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
752419Sjulian * All rights reserved.
870700Sjulian *
952419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
1052419Sjulian * redistribution of this software, in source or object code forms, with or
1152419Sjulian * without modifications are expressly permitted by Whistle Communications;
1252419Sjulian * provided, however, that:
1352419Sjulian * 1. Any and all reproductions of the source or object code must include the
1452419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1552419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1652419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1752419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1852419Sjulian *    such appears in the above copyright notice or in the software.
1970700Sjulian *
2052419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2152419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2252419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2352419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2452419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2552419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2652419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2752419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2852419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2952419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
3052419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3152419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3252419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3352419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3452419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3552419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3652419Sjulian * OF SUCH DAMAGE.
3752419Sjulian *
3867506Sjulian * Authors: Julian Elischer <julian@freebsd.org>
3967506Sjulian *          Archie Cobbs <archie@freebsd.org>
4052419Sjulian *
4152419Sjulian * $FreeBSD: head/sys/netgraph/ng_base.c 167385 2007-03-09 21:04:50Z julian $
4252419Sjulian * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
4352419Sjulian */
4452419Sjulian
4552419Sjulian/*
4652419Sjulian * This file implements the base netgraph code.
4752419Sjulian */
4852419Sjulian
4952419Sjulian#include <sys/param.h>
50139236Sglebius#include <sys/systm.h>
51139235Sglebius#include <sys/ctype.h>
5252419Sjulian#include <sys/errno.h>
53131933Smarcel#include <sys/kdb.h>
5452419Sjulian#include <sys/kernel.h>
55154225Sglebius#include <sys/ktr.h>
56114216Skan#include <sys/limits.h>
5752419Sjulian#include <sys/malloc.h>
58139235Sglebius#include <sys/mbuf.h>
5952419Sjulian#include <sys/queue.h>
6072946Sjulian#include <sys/sysctl.h>
61139235Sglebius#include <sys/syslog.h>
6252419Sjulian
6352419Sjulian#include <net/netisr.h>
6452419Sjulian
6552419Sjulian#include <netgraph/ng_message.h>
6652419Sjulian#include <netgraph/netgraph.h>
6753913Sarchie#include <netgraph/ng_parse.h>
6852419Sjulian
6972053SjulianMODULE_VERSION(netgraph, NG_ABI_VERSION);
7059756Speter
7170784Sjulian/* List of all active nodes */
7270700Sjulianstatic LIST_HEAD(, ng_node) ng_nodelist;
7370700Sjulianstatic struct mtx	ng_nodelist_mtx;
7452419Sjulian
75151974Sglebius/* Mutex to protect topology events. */
76151974Sglebiusstatic struct mtx	ng_topo_mtx;
77151974Sglebius
7870784Sjulian#ifdef	NETGRAPH_DEBUG
79152451Sglebiusstatic struct mtx	ngq_mtx;	/* protects the queue item list */
8070784Sjulian
8170784Sjulianstatic SLIST_HEAD(, ng_node) ng_allnodes;
8270784Sjulianstatic LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
8370784Sjulianstatic SLIST_HEAD(, ng_hook) ng_allhooks;
8470784Sjulianstatic LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
8570784Sjulian
8670784Sjulianstatic void ng_dumpitems(void);
8770784Sjulianstatic void ng_dumpnodes(void);
8870784Sjulianstatic void ng_dumphooks(void);
8970784Sjulian
9070784Sjulian#endif	/* NETGRAPH_DEBUG */
9170935Sjulian/*
92152451Sglebius * DEAD versions of the structures.
9370935Sjulian * In order to avoid races, it is sometimes neccesary to point
94152451Sglebius * at SOMETHING even though theoretically, the current entity is
9570935Sjulian * INVALID. Use these to avoid these races.
9670935Sjulian */
9770935Sjulianstruct ng_type ng_deadtype = {
9870935Sjulian	NG_ABI_VERSION,
9970935Sjulian	"dead",
10070935Sjulian	NULL,	/* modevent */
10170935Sjulian	NULL,	/* constructor */
10270935Sjulian	NULL,	/* rcvmsg */
10370935Sjulian	NULL,	/* shutdown */
10470935Sjulian	NULL,	/* newhook */
10570935Sjulian	NULL,	/* findhook */
10670935Sjulian	NULL,	/* connect */
10770935Sjulian	NULL,	/* rcvdata */
10870935Sjulian	NULL,	/* disconnect */
10970935Sjulian	NULL, 	/* cmdlist */
11070935Sjulian};
11170784Sjulian
11270935Sjulianstruct ng_node ng_deadnode = {
11370935Sjulian	"dead",
11470935Sjulian	&ng_deadtype,
115132464Sjulian	NGF_INVALID,
11670935Sjulian	1,	/* refs */
11770935Sjulian	0,	/* numhooks */
11870935Sjulian	NULL,	/* private */
11970935Sjulian	0,	/* ID */
12070935Sjulian	LIST_HEAD_INITIALIZER(ng_deadnode.hooks),
12170935Sjulian	{},	/* all_nodes list entry */
12270935Sjulian	{},	/* id hashtable list entry */
12370935Sjulian	{},	/* workqueue entry */
12470935Sjulian	{	0,
12570935Sjulian		{}, /* should never use! (should hang) */
12670935Sjulian		NULL,
12770935Sjulian		&ng_deadnode.nd_input_queue.queue,
12870935Sjulian		&ng_deadnode
12970935Sjulian	},
13070935Sjulian#ifdef	NETGRAPH_DEBUG
13170935Sjulian	ND_MAGIC,
13270935Sjulian	__FILE__,
13370935Sjulian	__LINE__,
13470935Sjulian	{NULL}
13570935Sjulian#endif	/* NETGRAPH_DEBUG */
13670935Sjulian};
13770935Sjulian
13870935Sjulianstruct ng_hook ng_deadhook = {
13970935Sjulian	"dead",
14070935Sjulian	NULL,		/* private */
14170935Sjulian	HK_INVALID | HK_DEAD,
14270935Sjulian	1,		/* refs always >= 1 */
143148261Sglebius	0,		/* undefined data link type */
14470935Sjulian	&ng_deadhook,	/* Peer is self */
14570935Sjulian	&ng_deadnode,	/* attached to deadnode */
14670935Sjulian	{},		/* hooks list */
14771885Sjulian	NULL,		/* override rcvmsg() */
14871885Sjulian	NULL,		/* override rcvdata() */
14970935Sjulian#ifdef	NETGRAPH_DEBUG
15070935Sjulian	HK_MAGIC,
15170935Sjulian	__FILE__,
15270935Sjulian	__LINE__,
15370935Sjulian	{NULL}
15470935Sjulian#endif	/* NETGRAPH_DEBUG */
15570935Sjulian};
15670935Sjulian
15770935Sjulian/*
15870935Sjulian * END DEAD STRUCTURES
15970935Sjulian */
16070700Sjulian/* List nodes with unallocated work */
16170700Sjulianstatic TAILQ_HEAD(, ng_node) ng_worklist = TAILQ_HEAD_INITIALIZER(ng_worklist);
16271902Sjulianstatic struct mtx	ng_worklist_mtx;   /* MUST LOCK NODE FIRST */
16370700Sjulian
16452419Sjulian/* List of installed types */
16570700Sjulianstatic LIST_HEAD(, ng_type) ng_typelist;
16670700Sjulianstatic struct mtx	ng_typelist_mtx;
16752419Sjulian
16870700Sjulian/* Hash related definitions */
16971354Sjulian/* XXX Don't need to initialise them because it's a LIST */
17071354Sjulian#define NG_ID_HASH_SIZE 32 /* most systems wont need even this many */
17171354Sjulianstatic LIST_HEAD(, ng_node) ng_ID_hash[NG_ID_HASH_SIZE];
17270700Sjulianstatic struct mtx	ng_idhash_mtx;
17371354Sjulian/* Method to find a node.. used twice so do it here */
17471354Sjulian#define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE))
17571354Sjulian#define NG_IDHASH_FIND(ID, node)					\
17671354Sjulian	do { 								\
177131008Srwatson		mtx_assert(&ng_idhash_mtx, MA_OWNED);			\
17871354Sjulian		LIST_FOREACH(node, &ng_ID_hash[NG_IDHASH_FN(ID)],	\
17971354Sjulian						nd_idnodes) {		\
18071354Sjulian			if (NG_NODE_IS_VALID(node)			\
18171354Sjulian			&& (NG_NODE_ID(node) == ID)) {			\
18271354Sjulian				break;					\
18371354Sjulian			}						\
18471354Sjulian		}							\
18571354Sjulian	} while (0)
18652722Sjulian
18770700Sjulian
18852419Sjulian/* Internal functions */
18952419Sjulianstatic int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
19070700Sjulianstatic int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
19152722Sjulianstatic ng_ID_t	ng_decodeidname(const char *name);
19252419Sjulianstatic int	ngb_mod_event(module_t mod, int event, void *data);
19370700Sjulianstatic void	ng_worklist_remove(node_p node);
19452419Sjulianstatic void	ngintr(void);
195167385Sjulianstatic void	ng_apply_item(node_p node, item_p item, int rw);
19670700Sjulianstatic void	ng_flush_input_queue(struct ng_queue * ngq);
19770700Sjulianstatic void	ng_setisr(node_p node);
19870700Sjulianstatic node_p	ng_ID2noderef(ng_ID_t ID);
19971047Sjulianstatic int	ng_con_nodes(node_p node, const char *name, node_p node2,
20071047Sjulian							const char *name2);
20171849Sjulianstatic void	ng_con_part2(node_p node, hook_p hook, void *arg1, int arg2);
20271849Sjulianstatic void	ng_con_part3(node_p node, hook_p hook, void *arg1, int arg2);
20371047Sjulianstatic int	ng_mkpeer(node_p node, const char *name,
20471047Sjulian						const char *name2, char *type);
20552419Sjulian
206152451Sglebius/* Imported, these used to be externally visible, some may go back. */
20770700Sjulianvoid	ng_destroy_hook(hook_p hook);
20870700Sjuliannode_p	ng_name2noderef(node_p node, const char *name);
20970700Sjulianint	ng_path2noderef(node_p here, const char *path,
21070700Sjulian	node_p *dest, hook_p *lasthook);
21170700Sjulianint	ng_make_node(const char *type, node_p *nodepp);
21270700Sjulianint	ng_path_parse(char *addr, char **node, char **path, char **hook);
21371849Sjulianvoid	ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3);
21470784Sjulianvoid	ng_unname(node_p node);
21570700Sjulian
21670700Sjulian
21752419Sjulian/* Our own netgraph malloc type */
21852419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
21970700SjulianMALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures");
22070700SjulianMALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures");
22170700SjulianMALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures");
22270700SjulianMALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
22352419Sjulian
22470700Sjulian/* Should not be visible outside this file */
22570784Sjulian
22670784Sjulian#define _NG_ALLOC_HOOK(hook) \
22770784Sjulian	MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
22870784Sjulian#define _NG_ALLOC_NODE(node) \
22970784Sjulian	MALLOC(node, node_p, sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
23070784Sjulian
23170784Sjulian#ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
23270784Sjulian/*
23370784Sjulian * In debug mode:
23470784Sjulian * In an attempt to help track reference count screwups
23570784Sjulian * we do not free objects back to the malloc system, but keep them
23670784Sjulian * in a local cache where we can examine them and keep information safely
23770784Sjulian * after they have been freed.
23870784Sjulian * We use this scheme for nodes and hooks, and to some extent for items.
23970784Sjulian */
24070784Sjulianstatic __inline hook_p
24170784Sjulianng_alloc_hook(void)
24270784Sjulian{
24370784Sjulian	hook_p hook;
24470784Sjulian	SLIST_ENTRY(ng_hook) temp;
24572200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
24670784Sjulian	hook = LIST_FIRST(&ng_freehooks);
24770784Sjulian	if (hook) {
24870784Sjulian		LIST_REMOVE(hook, hk_hooks);
24970784Sjulian		bcopy(&hook->hk_all, &temp, sizeof(temp));
25070784Sjulian		bzero(hook, sizeof(struct ng_hook));
25170784Sjulian		bcopy(&temp, &hook->hk_all, sizeof(temp));
25272200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
25370784Sjulian		hook->hk_magic = HK_MAGIC;
25470784Sjulian	} else {
25572200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
25670784Sjulian		_NG_ALLOC_HOOK(hook);
25770784Sjulian		if (hook) {
25870784Sjulian			hook->hk_magic = HK_MAGIC;
25972200Sbmilekic			mtx_lock(&ng_nodelist_mtx);
26070784Sjulian			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
26172200Sbmilekic			mtx_unlock(&ng_nodelist_mtx);
26270784Sjulian		}
26370784Sjulian	}
26470784Sjulian	return (hook);
26570784Sjulian}
26670784Sjulian
26770784Sjulianstatic __inline node_p
26870784Sjulianng_alloc_node(void)
26970784Sjulian{
27070784Sjulian	node_p node;
27170784Sjulian	SLIST_ENTRY(ng_node) temp;
27272200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
27370784Sjulian	node = LIST_FIRST(&ng_freenodes);
27470784Sjulian	if (node) {
27570784Sjulian		LIST_REMOVE(node, nd_nodes);
27670784Sjulian		bcopy(&node->nd_all, &temp, sizeof(temp));
27770784Sjulian		bzero(node, sizeof(struct ng_node));
27870784Sjulian		bcopy(&temp, &node->nd_all, sizeof(temp));
27972200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
28070784Sjulian		node->nd_magic = ND_MAGIC;
28170784Sjulian	} else {
28272200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
28370784Sjulian		_NG_ALLOC_NODE(node);
28470784Sjulian		if (node) {
28570784Sjulian			node->nd_magic = ND_MAGIC;
28672200Sbmilekic			mtx_lock(&ng_nodelist_mtx);
28770784Sjulian			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
28872200Sbmilekic			mtx_unlock(&ng_nodelist_mtx);
28970784Sjulian		}
29070784Sjulian	}
29170784Sjulian	return (node);
29270784Sjulian}
29370784Sjulian
29470784Sjulian#define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
29570784Sjulian#define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
29670784Sjulian
29770784Sjulian
29870784Sjulian#define NG_FREE_HOOK(hook)						\
29970784Sjulian	do {								\
30072200Sbmilekic		mtx_lock(&ng_nodelist_mtx);			\
30170784Sjulian		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
30270784Sjulian		hook->hk_magic = 0;					\
30372200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);			\
30470784Sjulian	} while (0)
30570784Sjulian
30670784Sjulian#define NG_FREE_NODE(node)						\
30770784Sjulian	do {								\
30872200Sbmilekic		mtx_lock(&ng_nodelist_mtx);			\
30970784Sjulian		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
31070784Sjulian		node->nd_magic = 0;					\
31172200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);			\
31270784Sjulian	} while (0)
31370784Sjulian
31470784Sjulian#else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
31570784Sjulian
31670784Sjulian#define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
31770784Sjulian#define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
31870784Sjulian
31970700Sjulian#define NG_FREE_HOOK(hook) do { FREE((hook), M_NETGRAPH_HOOK); } while (0)
32070700Sjulian#define NG_FREE_NODE(node) do { FREE((node), M_NETGRAPH_NODE); } while (0)
32170784Sjulian
32270784Sjulian#endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
32370784Sjulian
324131933Smarcel/* Set this to kdb_enter("X") to catch all errors as they occur */
32552419Sjulian#ifndef TRAP_ERROR
32671047Sjulian#define TRAP_ERROR()
32752419Sjulian#endif
32852419Sjulian
32952722Sjulianstatic	ng_ID_t nextID = 1;
33052722Sjulian
33153403Sarchie#ifdef INVARIANTS
33253403Sarchie#define CHECK_DATA_MBUF(m)	do {					\
33353403Sarchie		struct mbuf *n;						\
33453403Sarchie		int total;						\
33553403Sarchie									\
336113255Sdes		M_ASSERTPKTHDR(m);					\
337149818Sglebius		for (total = 0, n = (m); n != NULL; n = n->m_next) {	\
33853403Sarchie			total += n->m_len;				\
339149818Sglebius			if (n->m_nextpkt != NULL)			\
340149818Sglebius				panic("%s: m_nextpkt", __func__);	\
341149818Sglebius		}							\
342149827Sglebius									\
34353403Sarchie		if ((m)->m_pkthdr.len != total) {			\
34453403Sarchie			panic("%s: %d != %d",				\
34587599Sobrien			    __func__, (m)->m_pkthdr.len, total);	\
34653403Sarchie		}							\
34753403Sarchie	} while (0)
34853403Sarchie#else
34953403Sarchie#define CHECK_DATA_MBUF(m)
35053403Sarchie#endif
35152722Sjulian
35253403Sarchie
35352419Sjulian/************************************************************************
35453913Sarchie	Parse type definitions for generic messages
35553913Sarchie************************************************************************/
35653913Sarchie
35753913Sarchie/* Handy structure parse type defining macro */
35853913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
35997685Sarchiestatic const struct ng_parse_struct_field				\
36097685Sarchie	ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args;	\
36153913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
36253913Sarchie	&ng_parse_struct_type,						\
36397685Sarchie	&ng_ ## lo ## _type_fields					\
36453913Sarchie}
36553913Sarchie
36653913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
36753913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
36853913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
36953913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
37053913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
37153913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
37253913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
37353913Sarchie
37453913Sarchie/* Get length of an array when the length is stored as a 32 bit
37572645Sasmodai   value immediately preceding the array -- as with struct namelist
37653913Sarchie   and struct typelist. */
37753913Sarchiestatic int
37853913Sarchieng_generic_list_getLength(const struct ng_parse_type *type,
37953913Sarchie	const u_char *start, const u_char *buf)
38053913Sarchie{
38153913Sarchie	return *((const u_int32_t *)(buf - 4));
38253913Sarchie}
38353913Sarchie
38453913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */
38553913Sarchiestatic int
38653913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type,
38753913Sarchie	const u_char *start, const u_char *buf)
38853913Sarchie{
38953913Sarchie	const struct hooklist *hl = (const struct hooklist *)start;
39053913Sarchie
39153913Sarchie	return hl->nodeinfo.hooks;
39253913Sarchie}
39353913Sarchie
39453913Sarchie/* Array type for a variable length array of struct namelist */
39553913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
39653913Sarchie	&ng_generic_nodeinfo_type,
39753913Sarchie	&ng_generic_list_getLength
39853913Sarchie};
39953913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = {
40053913Sarchie	&ng_parse_array_type,
40153913Sarchie	&ng_nodeinfoarray_type_info
40253913Sarchie};
40353913Sarchie
40453913Sarchie/* Array type for a variable length array of struct typelist */
40553913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = {
40653913Sarchie	&ng_generic_typeinfo_type,
40753913Sarchie	&ng_generic_list_getLength
40853913Sarchie};
40953913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = {
41053913Sarchie	&ng_parse_array_type,
41153913Sarchie	&ng_typeinfoarray_type_info
41253913Sarchie};
41353913Sarchie
41453913Sarchie/* Array type for array of struct linkinfo in struct hooklist */
41553913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
41653913Sarchie	&ng_generic_linkinfo_type,
41753913Sarchie	&ng_generic_linkinfo_getLength
41853913Sarchie};
41953913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = {
42053913Sarchie	&ng_parse_array_type,
42153913Sarchie	&ng_generic_linkinfo_array_type_info
42253913Sarchie};
42353913Sarchie
42453913SarchieDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
42553913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
42653913Sarchie	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
42753913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
42853913Sarchie	(&ng_generic_nodeinfoarray_type));
42953913Sarchie
43053913Sarchie/* List of commands and how to convert arguments to/from ASCII */
43153913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = {
43253913Sarchie	{
43353913Sarchie	  NGM_GENERIC_COOKIE,
43453913Sarchie	  NGM_SHUTDOWN,
43553913Sarchie	  "shutdown",
43653913Sarchie	  NULL,
43753913Sarchie	  NULL
43853913Sarchie	},
43953913Sarchie	{
44053913Sarchie	  NGM_GENERIC_COOKIE,
44153913Sarchie	  NGM_MKPEER,
44253913Sarchie	  "mkpeer",
44353913Sarchie	  &ng_generic_mkpeer_type,
44453913Sarchie	  NULL
44553913Sarchie	},
44653913Sarchie	{
44753913Sarchie	  NGM_GENERIC_COOKIE,
44853913Sarchie	  NGM_CONNECT,
44953913Sarchie	  "connect",
45053913Sarchie	  &ng_generic_connect_type,
45153913Sarchie	  NULL
45253913Sarchie	},
45353913Sarchie	{
45453913Sarchie	  NGM_GENERIC_COOKIE,
45553913Sarchie	  NGM_NAME,
45653913Sarchie	  "name",
45753913Sarchie	  &ng_generic_name_type,
45853913Sarchie	  NULL
45953913Sarchie	},
46053913Sarchie	{
46153913Sarchie	  NGM_GENERIC_COOKIE,
46253913Sarchie	  NGM_RMHOOK,
46353913Sarchie	  "rmhook",
46453913Sarchie	  &ng_generic_rmhook_type,
46553913Sarchie	  NULL
46653913Sarchie	},
46753913Sarchie	{
46853913Sarchie	  NGM_GENERIC_COOKIE,
46953913Sarchie	  NGM_NODEINFO,
47053913Sarchie	  "nodeinfo",
47153913Sarchie	  NULL,
47253913Sarchie	  &ng_generic_nodeinfo_type
47353913Sarchie	},
47453913Sarchie	{
47553913Sarchie	  NGM_GENERIC_COOKIE,
47653913Sarchie	  NGM_LISTHOOKS,
47753913Sarchie	  "listhooks",
47853913Sarchie	  NULL,
47953913Sarchie	  &ng_generic_hooklist_type
48053913Sarchie	},
48153913Sarchie	{
48253913Sarchie	  NGM_GENERIC_COOKIE,
48353913Sarchie	  NGM_LISTNAMES,
48453913Sarchie	  "listnames",
48553913Sarchie	  NULL,
48653913Sarchie	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
48753913Sarchie	},
48853913Sarchie	{
48953913Sarchie	  NGM_GENERIC_COOKIE,
49053913Sarchie	  NGM_LISTNODES,
49153913Sarchie	  "listnodes",
49253913Sarchie	  NULL,
49353913Sarchie	  &ng_generic_listnodes_type
49453913Sarchie	},
49553913Sarchie	{
49653913Sarchie	  NGM_GENERIC_COOKIE,
49753913Sarchie	  NGM_LISTTYPES,
49853913Sarchie	  "listtypes",
49953913Sarchie	  NULL,
50053913Sarchie	  &ng_generic_typeinfo_type
50153913Sarchie	},
50253913Sarchie	{
50353913Sarchie	  NGM_GENERIC_COOKIE,
50462471Sphk	  NGM_TEXT_CONFIG,
50562471Sphk	  "textconfig",
50662471Sphk	  NULL,
50762471Sphk	  &ng_parse_string_type
50862471Sphk	},
50962471Sphk	{
51062471Sphk	  NGM_GENERIC_COOKIE,
51153913Sarchie	  NGM_TEXT_STATUS,
51253913Sarchie	  "textstatus",
51353913Sarchie	  NULL,
51453913Sarchie	  &ng_parse_string_type
51553913Sarchie	},
51653913Sarchie	{
51753913Sarchie	  NGM_GENERIC_COOKIE,
51853913Sarchie	  NGM_ASCII2BINARY,
51953913Sarchie	  "ascii2binary",
52053913Sarchie	  &ng_parse_ng_mesg_type,
52153913Sarchie	  &ng_parse_ng_mesg_type
52253913Sarchie	},
52353913Sarchie	{
52453913Sarchie	  NGM_GENERIC_COOKIE,
52553913Sarchie	  NGM_BINARY2ASCII,
52653913Sarchie	  "binary2ascii",
52753913Sarchie	  &ng_parse_ng_mesg_type,
52853913Sarchie	  &ng_parse_ng_mesg_type
52953913Sarchie	},
53053913Sarchie	{ 0 }
53153913Sarchie};
53253913Sarchie
53353913Sarchie/************************************************************************
53452419Sjulian			Node routines
53552419Sjulian************************************************************************/
53652419Sjulian
53752419Sjulian/*
53852419Sjulian * Instantiate a node of the requested type
53952419Sjulian */
54052419Sjulianint
54152419Sjulianng_make_node(const char *typename, node_p *nodepp)
54252419Sjulian{
54352419Sjulian	struct ng_type *type;
54470700Sjulian	int	error;
54552419Sjulian
54652419Sjulian	/* Check that the type makes sense */
54752419Sjulian	if (typename == NULL) {
54871047Sjulian		TRAP_ERROR();
54952419Sjulian		return (EINVAL);
55052419Sjulian	}
55152419Sjulian
552132705Sglebius	/* Locate the node type. If we fail we return. Do not try to load
553132705Sglebius	 * module.
554132705Sglebius	 */
555132705Sglebius	if ((type = ng_findtype(typename)) == NULL)
556132705Sglebius		return (ENXIO);
55752419Sjulian
55870700Sjulian	/*
55970700Sjulian	 * If we have a constructor, then make the node and
56070700Sjulian	 * call the constructor to do type specific initialisation.
56170700Sjulian	 */
56270700Sjulian	if (type->constructor != NULL) {
56370700Sjulian		if ((error = ng_make_node_common(type, nodepp)) == 0) {
56470700Sjulian			if ((error = ((*type->constructor)(*nodepp)) != 0)) {
56570784Sjulian				NG_NODE_UNREF(*nodepp);
56670700Sjulian			}
56770700Sjulian		}
56870700Sjulian	} else {
56970700Sjulian		/*
57070700Sjulian		 * Node has no constructor. We cannot ask for one
57170700Sjulian		 * to be made. It must be brought into existance by
57270935Sjulian		 * some external agency. The external agency should
57370700Sjulian		 * call ng_make_node_common() directly to get the
57470700Sjulian		 * netgraph part initialised.
57570700Sjulian		 */
57671047Sjulian		TRAP_ERROR();
57770700Sjulian		error = EINVAL;
57870700Sjulian	}
57970700Sjulian	return (error);
58052419Sjulian}
58152419Sjulian
58252419Sjulian/*
58370700Sjulian * Generic node creation. Called by node initialisation for externally
58470700Sjulian * instantiated nodes (e.g. hardware, sockets, etc ).
58552419Sjulian * The returned node has a reference count of 1.
58652419Sjulian */
58752419Sjulianint
58852419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp)
58952419Sjulian{
59052419Sjulian	node_p node;
59152419Sjulian
59252419Sjulian	/* Require the node type to have been already installed */
59352419Sjulian	if (ng_findtype(type->name) == NULL) {
59471047Sjulian		TRAP_ERROR();
59552419Sjulian		return (EINVAL);
59652419Sjulian	}
59752419Sjulian
59852419Sjulian	/* Make a node and try attach it to the type */
59970784Sjulian	NG_ALLOC_NODE(node);
60052419Sjulian	if (node == NULL) {
60171047Sjulian		TRAP_ERROR();
60252419Sjulian		return (ENOMEM);
60352419Sjulian	}
60470784Sjulian	node->nd_type = type;
60570784Sjulian	NG_NODE_REF(node);				/* note reference */
60652419Sjulian	type->refs++;
60752419Sjulian
60893818Sjhb	mtx_init(&node->nd_input_queue.q_mtx, "ng_node", NULL, MTX_SPIN);
60970784Sjulian	node->nd_input_queue.queue = NULL;
61070784Sjulian	node->nd_input_queue.last = &node->nd_input_queue.queue;
61170784Sjulian	node->nd_input_queue.q_flags = 0;
61270784Sjulian	node->nd_input_queue.q_node = node;
61352419Sjulian
61452419Sjulian	/* Initialize hook list for new node */
61570784Sjulian	LIST_INIT(&node->nd_hooks);
61652419Sjulian
61770700Sjulian	/* Link us into the node linked list */
61872200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
61970784Sjulian	LIST_INSERT_HEAD(&ng_nodelist, node, nd_nodes);
62072200Sbmilekic	mtx_unlock(&ng_nodelist_mtx);
62170700Sjulian
62270700Sjulian
62352722Sjulian	/* get an ID and put us in the hash chain */
62472200Sbmilekic	mtx_lock(&ng_idhash_mtx);
62570784Sjulian	for (;;) { /* wrap protection, even if silly */
62670700Sjulian		node_p node2 = NULL;
62770784Sjulian		node->nd_ID = nextID++; /* 137/second for 1 year before wrap */
62871354Sjulian
62970784Sjulian		/* Is there a problem with the new number? */
63071354Sjulian		NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */
63171354Sjulian		if ((node->nd_ID != 0) && (node2 == NULL)) {
63270784Sjulian			break;
63370700Sjulian		}
63470784Sjulian	}
63571354Sjulian	LIST_INSERT_HEAD(&ng_ID_hash[NG_IDHASH_FN(node->nd_ID)],
63670784Sjulian							node, nd_idnodes);
63772200Sbmilekic	mtx_unlock(&ng_idhash_mtx);
63852722Sjulian
63952419Sjulian	/* Done */
64052419Sjulian	*nodepp = node;
64152419Sjulian	return (0);
64252419Sjulian}
64352419Sjulian
64452419Sjulian/*
64552419Sjulian * Forceably start the shutdown process on a node. Either call
64652419Sjulian * it's shutdown method, or do the default shutdown if there is
64752419Sjulian * no type-specific method.
64852419Sjulian *
64970700Sjulian * We can only be called form a shutdown message, so we know we have
65070939Sjulian * a writer lock, and therefore exclusive access. It also means
65170939Sjulian * that we should not be on the work queue, but we check anyhow.
65270700Sjulian *
65370700Sjulian * Persistent node types must have a type-specific method which
65470939Sjulian * Allocates a new node in which case, this one is irretrievably going away,
65570939Sjulian * or cleans up anything it needs, and just makes the node valid again,
656152451Sglebius * in which case we allow the node to survive.
65770939Sjulian *
65870939Sjulian * XXX We need to think of how to tell a persistant node that we
65970939Sjulian * REALLY need to go away because the hardware has gone or we
66070939Sjulian * are rebooting.... etc.
66152419Sjulian */
66252419Sjulianvoid
66371849Sjulianng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3)
66452419Sjulian{
66570939Sjulian	hook_p hook;
66670939Sjulian
66752419Sjulian	/* Check if it's already shutting down */
668132464Sjulian	if ((node->nd_flags & NGF_CLOSING) != 0)
66952419Sjulian		return;
67052419Sjulian
67171849Sjulian	if (node == &ng_deadnode) {
67271849Sjulian		printf ("shutdown called on deadnode\n");
67371849Sjulian		return;
67471849Sjulian	}
67571849Sjulian
67652419Sjulian	/* Add an extra reference so it doesn't go away during this */
67770784Sjulian	NG_NODE_REF(node);
67852419Sjulian
67970784Sjulian	/*
68070784Sjulian	 * Mark it invalid so any newcomers know not to try use it
68170784Sjulian	 * Also add our own mark so we can't recurse
682132464Sjulian	 * note that NGF_INVALID does not do this as it's also set during
68370784Sjulian	 * creation
68470784Sjulian	 */
685132464Sjulian	node->nd_flags |= NGF_INVALID|NGF_CLOSING;
68652419Sjulian
687129836Sjulian	/* If node has its pre-shutdown method, then call it first*/
688129836Sjulian	if (node->nd_type && node->nd_type->close)
689129836Sjulian		(*node->nd_type->close)(node);
690129836Sjulian
69170939Sjulian	/* Notify all remaining connected nodes to disconnect */
69270939Sjulian	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
69370939Sjulian		ng_destroy_hook(hook);
69470784Sjulian
69570700Sjulian	/*
69670700Sjulian	 * Drain the input queue forceably.
69770784Sjulian	 * it has no hooks so what's it going to do, bleed on someone?
69870784Sjulian	 * Theoretically we came here from a queue entry that was added
69970784Sjulian	 * Just before the queue was closed, so it should be empty anyway.
70071902Sjulian	 * Also removes us from worklist if needed.
70170700Sjulian	 */
70270784Sjulian	ng_flush_input_queue(&node->nd_input_queue);
70370700Sjulian
70452419Sjulian	/* Ask the type if it has anything to do in this case */
70570784Sjulian	if (node->nd_type && node->nd_type->shutdown) {
70670784Sjulian		(*node->nd_type->shutdown)(node);
70771849Sjulian		if (NG_NODE_IS_VALID(node)) {
70871849Sjulian			/*
70971849Sjulian			 * Well, blow me down if the node code hasn't declared
71071849Sjulian			 * that it doesn't want to die.
71171849Sjulian			 * Presumably it is a persistant node.
71271849Sjulian			 * If we REALLY want it to go away,
71371849Sjulian			 *  e.g. hardware going away,
714132464Sjulian			 * Our caller should set NGF_REALLY_DIE in nd_flags.
715152451Sglebius			 */
716132464Sjulian			node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING);
71771849Sjulian			NG_NODE_UNREF(node); /* Assume they still have theirs */
71871849Sjulian			return;
71971849Sjulian		}
72070700Sjulian	} else {				/* do the default thing */
72170784Sjulian		NG_NODE_UNREF(node);
72252419Sjulian	}
72352419Sjulian
72470784Sjulian	ng_unname(node); /* basically a NOP these days */
72570784Sjulian
72670784Sjulian	/*
72770784Sjulian	 * Remove extra reference, possibly the last
72870784Sjulian	 * Possible other holders of references may include
72970784Sjulian	 * timeout callouts, but theoretically the node's supposed to
73070784Sjulian	 * have cancelled them. Possibly hardware dependencies may
73170784Sjulian	 * force a driver to 'linger' with a reference.
73270784Sjulian	 */
73370784Sjulian	NG_NODE_UNREF(node);
73452419Sjulian}
73552419Sjulian
73652419Sjulian/*
73774078Sjulian * Remove a reference to the node, possibly the last.
73874078Sjulian * deadnode always acts as it it were the last.
73952419Sjulian */
74074078Sjulianint
74170784Sjulianng_unref_node(node_p node)
74252419Sjulian{
743152451Sglebius	int v;
74471047Sjulian
74571047Sjulian	if (node == &ng_deadnode) {
74674078Sjulian		return (0);
74771047Sjulian	}
74871047Sjulian
74970784Sjulian	do {
75074078Sjulian		v = node->nd_refs - 1;
75174078Sjulian	} while (! atomic_cmpset_int(&node->nd_refs, v + 1, v));
75269519Sjulian
75374078Sjulian	if (v == 0) { /* we were the last */
75470700Sjulian
75572200Sbmilekic		mtx_lock(&ng_nodelist_mtx);
75670784Sjulian		node->nd_type->refs--; /* XXX maybe should get types lock? */
75770784Sjulian		LIST_REMOVE(node, nd_nodes);
75872200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
75970700Sjulian
76072200Sbmilekic		mtx_lock(&ng_idhash_mtx);
76170784Sjulian		LIST_REMOVE(node, nd_idnodes);
76272200Sbmilekic		mtx_unlock(&ng_idhash_mtx);
76352419Sjulian
76470791Sjulian		mtx_destroy(&node->nd_input_queue.q_mtx);
76570700Sjulian		NG_FREE_NODE(node);
76652419Sjulian	}
76774078Sjulian	return (v);
76852419Sjulian}
76952419Sjulian
77052419Sjulian/************************************************************************
77152722Sjulian			Node ID handling
77252722Sjulian************************************************************************/
77352722Sjulianstatic node_p
77470700Sjulianng_ID2noderef(ng_ID_t ID)
77552722Sjulian{
77670784Sjulian	node_p node;
77772200Sbmilekic	mtx_lock(&ng_idhash_mtx);
77871354Sjulian	NG_IDHASH_FIND(ID, node);
77970784Sjulian	if(node)
78070784Sjulian		NG_NODE_REF(node);
78172200Sbmilekic	mtx_unlock(&ng_idhash_mtx);
78270784Sjulian	return(node);
78352722Sjulian}
78452722Sjulian
78552722Sjulianng_ID_t
78652722Sjulianng_node2ID(node_p node)
78752722Sjulian{
78870912Sjulian	return (node ? NG_NODE_ID(node) : 0);
78952722Sjulian}
79052722Sjulian
79152722Sjulian/************************************************************************
79252419Sjulian			Node name handling
79352419Sjulian************************************************************************/
79452419Sjulian
79552419Sjulian/*
79652419Sjulian * Assign a node a name. Once assigned, the name cannot be changed.
79752419Sjulian */
79852419Sjulianint
79952419Sjulianng_name_node(node_p node, const char *name)
80052419Sjulian{
80152419Sjulian	int i;
80270700Sjulian	node_p node2;
80352419Sjulian
80452419Sjulian	/* Check the name is valid */
805125028Sharti	for (i = 0; i < NG_NODESIZ; i++) {
80652419Sjulian		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
80752419Sjulian			break;
80852419Sjulian	}
80952419Sjulian	if (i == 0 || name[i] != '\0') {
81071047Sjulian		TRAP_ERROR();
81152419Sjulian		return (EINVAL);
81252419Sjulian	}
81352722Sjulian	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
81471047Sjulian		TRAP_ERROR();
81552419Sjulian		return (EINVAL);
81652419Sjulian	}
81752419Sjulian
81852419Sjulian	/* Check the name isn't already being used */
81970700Sjulian	if ((node2 = ng_name2noderef(node, name)) != NULL) {
82070784Sjulian		NG_NODE_UNREF(node2);
82171047Sjulian		TRAP_ERROR();
82252419Sjulian		return (EADDRINUSE);
82352419Sjulian	}
82452419Sjulian
82570700Sjulian	/* copy it */
826125028Sharti	strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ);
82752419Sjulian
82852419Sjulian	return (0);
82952419Sjulian}
83052419Sjulian
83152419Sjulian/*
83252419Sjulian * Find a node by absolute name. The name should NOT end with ':'
83352419Sjulian * The name "." means "this node" and "[xxx]" means "the node
83452419Sjulian * with ID (ie, at address) xxx".
83552419Sjulian *
83652419Sjulian * Returns the node if found, else NULL.
83770700Sjulian * Eventually should add something faster than a sequential search.
83870784Sjulian * Note it aquires a reference on the node so you can be sure it's still there.
83952419Sjulian */
84052419Sjuliannode_p
84170700Sjulianng_name2noderef(node_p here, const char *name)
84252419Sjulian{
84352722Sjulian	node_p node;
84452722Sjulian	ng_ID_t temp;
84552419Sjulian
84652419Sjulian	/* "." means "this node" */
84770700Sjulian	if (strcmp(name, ".") == 0) {
84870784Sjulian		NG_NODE_REF(here);
84970700Sjulian		return(here);
85070700Sjulian	}
85152419Sjulian
85252419Sjulian	/* Check for name-by-ID */
85352722Sjulian	if ((temp = ng_decodeidname(name)) != 0) {
85470700Sjulian		return (ng_ID2noderef(temp));
85552419Sjulian	}
85652419Sjulian
85752419Sjulian	/* Find node by name */
85872200Sbmilekic	mtx_lock(&ng_nodelist_mtx);
85970784Sjulian	LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
86070912Sjulian		if (NG_NODE_IS_VALID(node)
86170912Sjulian		&& NG_NODE_HAS_NAME(node)
86270912Sjulian		&& (strcmp(NG_NODE_NAME(node), name) == 0)) {
86352419Sjulian			break;
86470912Sjulian		}
86552419Sjulian	}
86670700Sjulian	if (node)
86770784Sjulian		NG_NODE_REF(node);
86872200Sbmilekic	mtx_unlock(&ng_nodelist_mtx);
86952419Sjulian	return (node);
87052419Sjulian}
87152419Sjulian
87252419Sjulian/*
873108533Sschweikh * Decode an ID name, eg. "[f03034de]". Returns 0 if the
87452722Sjulian * string is not valid, otherwise returns the value.
87552419Sjulian */
87652722Sjulianstatic ng_ID_t
87752419Sjulianng_decodeidname(const char *name)
87852419Sjulian{
87952816Sarchie	const int len = strlen(name);
88053648Sarchie	char *eptr;
88152816Sarchie	u_long val;
88252419Sjulian
88352816Sarchie	/* Check for proper length, brackets, no leading junk */
88470912Sjulian	if ((len < 3)
88570912Sjulian	|| (name[0] != '[')
88670912Sjulian	|| (name[len - 1] != ']')
88770912Sjulian	|| (!isxdigit(name[1]))) {
88870912Sjulian		return ((ng_ID_t)0);
88970912Sjulian	}
89052419Sjulian
89152816Sarchie	/* Decode number */
89252816Sarchie	val = strtoul(name + 1, &eptr, 16);
89370912Sjulian	if ((eptr - name != len - 1)
89470912Sjulian	|| (val == ULONG_MAX)
89570912Sjulian	|| (val == 0)) {
89653042Sjulian		return ((ng_ID_t)0);
89770912Sjulian	}
89852816Sarchie	return (ng_ID_t)val;
89952419Sjulian}
90052419Sjulian
90152419Sjulian/*
90252419Sjulian * Remove a name from a node. This should only be called
90352419Sjulian * when shutting down and removing the node.
90471849Sjulian * IF we allow name changing this may be more resurected.
90552419Sjulian */
90652419Sjulianvoid
90752419Sjulianng_unname(node_p node)
90852419Sjulian{
90952419Sjulian}
91052419Sjulian
91152419Sjulian/************************************************************************
91252419Sjulian			Hook routines
91352419Sjulian Names are not optional. Hooks are always connected, except for a
91470939Sjulian brief moment within these routines. On invalidation or during creation
91570939Sjulian they are connected to the 'dead' hook.
91652419Sjulian************************************************************************/
91752419Sjulian
91852419Sjulian/*
91952419Sjulian * Remove a hook reference
92052419Sjulian */
92170784Sjulianvoid
92252419Sjulianng_unref_hook(hook_p hook)
92352419Sjulian{
924152451Sglebius	int v;
92571047Sjulian
92671047Sjulian	if (hook == &ng_deadhook) {
92771047Sjulian		return;
92871047Sjulian	}
92970784Sjulian	do {
93070784Sjulian		v = hook->hk_refs;
93170784Sjulian	} while (! atomic_cmpset_int(&hook->hk_refs, v, v - 1));
93269519Sjulian
93370784Sjulian	if (v == 1) { /* we were the last */
93471047Sjulian		if (_NG_HOOK_NODE(hook)) { /* it'll probably be ng_deadnode */
93571047Sjulian			_NG_NODE_UNREF((_NG_HOOK_NODE(hook)));
93670784Sjulian			hook->hk_node = NULL;
93770700Sjulian		}
93870700Sjulian		NG_FREE_HOOK(hook);
93970700Sjulian	}
94052419Sjulian}
94152419Sjulian
94252419Sjulian/*
94352419Sjulian * Add an unconnected hook to a node. Only used internally.
94470939Sjulian * Assumes node is locked. (XXX not yet true )
94552419Sjulian */
94652419Sjulianstatic int
94752419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp)
94852419Sjulian{
94952419Sjulian	hook_p hook;
95052419Sjulian	int error = 0;
95152419Sjulian
95252419Sjulian	/* Check that the given name is good */
95352419Sjulian	if (name == NULL) {
95471047Sjulian		TRAP_ERROR();
95552419Sjulian		return (EINVAL);
95652419Sjulian	}
95754096Sarchie	if (ng_findhook(node, name) != NULL) {
95871047Sjulian		TRAP_ERROR();
95954096Sarchie		return (EEXIST);
96052419Sjulian	}
96152419Sjulian
96252419Sjulian	/* Allocate the hook and link it up */
96370784Sjulian	NG_ALLOC_HOOK(hook);
96452419Sjulian	if (hook == NULL) {
96571047Sjulian		TRAP_ERROR();
96652419Sjulian		return (ENOMEM);
96752419Sjulian	}
96870939Sjulian	hook->hk_refs = 1;		/* add a reference for us to return */
96970784Sjulian	hook->hk_flags = HK_INVALID;
97070939Sjulian	hook->hk_peer = &ng_deadhook;	/* start off this way */
97170784Sjulian	hook->hk_node = node;
97270784Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
97352419Sjulian
97470939Sjulian	/* Set hook name */
975125028Sharti	strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ);
97670939Sjulian
97770939Sjulian	/*
97870939Sjulian	 * Check if the node type code has something to say about it
97970939Sjulian	 * If it fails, the unref of the hook will also unref the node.
98070939Sjulian	 */
98170935Sjulian	if (node->nd_type->newhook != NULL) {
98270935Sjulian		if ((error = (*node->nd_type->newhook)(node, hook, name))) {
98370784Sjulian			NG_HOOK_UNREF(hook);	/* this frees the hook */
98470700Sjulian			return (error);
98570700Sjulian		}
98670935Sjulian	}
98752419Sjulian	/*
98852419Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
98952419Sjulian	 * We'll ask again later when we actually connect the hooks.
99052419Sjulian	 */
99170784Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
99270784Sjulian	node->nd_numhooks++;
99370939Sjulian	NG_HOOK_REF(hook);	/* one for the node */
99452419Sjulian
99552419Sjulian	if (hookp)
99652419Sjulian		*hookp = hook;
99770939Sjulian	return (0);
99852419Sjulian}
99952419Sjulian
100052419Sjulian/*
100154096Sarchie * Find a hook
100254096Sarchie *
100354096Sarchie * Node types may supply their own optimized routines for finding
100454096Sarchie * hooks.  If none is supplied, we just do a linear search.
100570939Sjulian * XXX Possibly we should add a reference to the hook?
100654096Sarchie */
100754096Sarchiehook_p
100854096Sarchieng_findhook(node_p node, const char *name)
100954096Sarchie{
101054096Sarchie	hook_p hook;
101154096Sarchie
101270784Sjulian	if (node->nd_type->findhook != NULL)
101370784Sjulian		return (*node->nd_type->findhook)(node, name);
101470784Sjulian	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
101570912Sjulian		if (NG_HOOK_IS_VALID(hook)
101670917Sarchie		&& (strcmp(NG_HOOK_NAME(hook), name) == 0))
101754096Sarchie			return (hook);
101854096Sarchie	}
101954096Sarchie	return (NULL);
102054096Sarchie}
102154096Sarchie
102254096Sarchie/*
102352419Sjulian * Destroy a hook
102452419Sjulian *
102552419Sjulian * As hooks are always attached, this really destroys two hooks.
102652419Sjulian * The one given, and the one attached to it. Disconnect the hooks
102770939Sjulian * from each other first. We reconnect the peer hook to the 'dead'
102870939Sjulian * hook so that it can still exist after we depart. We then
102970939Sjulian * send the peer its own destroy message. This ensures that we only
1030152451Sglebius * interact with the peer's structures when it is locked processing that
103170939Sjulian * message. We hold a reference to the peer hook so we are guaranteed that
103270939Sjulian * the peer hook and node are still going to exist until
103370939Sjulian * we are finished there as the hook holds a ref on the node.
1034152451Sglebius * We run this same code again on the peer hook, but that time it is already
1035152451Sglebius * attached to the 'dead' hook.
103671047Sjulian *
1037152451Sglebius * This routine is called at all stages of hook creation
103871047Sjulian * on error detection and must be able to handle any such stage.
103952419Sjulian */
104052419Sjulianvoid
104152419Sjulianng_destroy_hook(hook_p hook)
104252419Sjulian{
1043151974Sglebius	hook_p peer;
1044151974Sglebius	node_p node;
104552419Sjulian
104671047Sjulian	if (hook == &ng_deadhook) {	/* better safe than sorry */
104771047Sjulian		printf("ng_destroy_hook called on deadhook\n");
104871047Sjulian		return;
104971047Sjulian	}
1050151974Sglebius
1051151974Sglebius	/*
1052151974Sglebius	 * Protect divorce process with mutex, to avoid races on
1053151974Sglebius	 * simultaneous disconnect.
1054151974Sglebius	 */
1055151974Sglebius	mtx_lock(&ng_topo_mtx);
1056151974Sglebius
1057151974Sglebius	hook->hk_flags |= HK_INVALID;
1058151974Sglebius
1059151974Sglebius	peer = NG_HOOK_PEER(hook);
1060151974Sglebius	node = NG_HOOK_NODE(hook);
1061151974Sglebius
106270939Sjulian	if (peer && (peer != &ng_deadhook)) {
106370939Sjulian		/*
106470939Sjulian		 * Set the peer to point to ng_deadhook
106570939Sjulian		 * from this moment on we are effectively independent it.
106670939Sjulian		 * send it an rmhook message of it's own.
106770939Sjulian		 */
106870939Sjulian		peer->hk_peer = &ng_deadhook;	/* They no longer know us */
106970939Sjulian		hook->hk_peer = &ng_deadhook;	/* Nor us, them */
107071047Sjulian		if (NG_HOOK_NODE(peer) == &ng_deadnode) {
1071152451Sglebius			/*
107271047Sjulian			 * If it's already divorced from a node,
107371047Sjulian			 * just free it.
107471047Sjulian			 */
1075151974Sglebius			mtx_unlock(&ng_topo_mtx);
107671047Sjulian		} else {
1077151974Sglebius			mtx_unlock(&ng_topo_mtx);
107871047Sjulian			ng_rmhook_self(peer); 	/* Send it a surprise */
107971047Sjulian		}
108070942Sjulian		NG_HOOK_UNREF(peer);		/* account for peer link */
108170942Sjulian		NG_HOOK_UNREF(hook);		/* account for peer link */
1082151974Sglebius	} else
1083151974Sglebius		mtx_unlock(&ng_topo_mtx);
108452419Sjulian
1085151974Sglebius	mtx_assert(&ng_topo_mtx, MA_NOTOWNED);
1086151974Sglebius
108752419Sjulian	/*
108852419Sjulian	 * Remove the hook from the node's list to avoid possible recursion
108952419Sjulian	 * in case the disconnection results in node shutdown.
109052419Sjulian	 */
109171047Sjulian	if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */
109271047Sjulian		return;
109371047Sjulian	}
109470784Sjulian	LIST_REMOVE(hook, hk_hooks);
109570784Sjulian	node->nd_numhooks--;
109670784Sjulian	if (node->nd_type->disconnect) {
109752419Sjulian		/*
109871047Sjulian		 * The type handler may elect to destroy the node so don't
1099152451Sglebius		 * trust its existance after this point. (except
110071047Sjulian		 * that we still hold a reference on it. (which we
110171047Sjulian		 * inherrited from the hook we are destroying)
110252419Sjulian		 */
110370784Sjulian		(*node->nd_type->disconnect) (hook);
110452419Sjulian	}
110571047Sjulian
110671047Sjulian	/*
110771047Sjulian	 * Note that because we will point to ng_deadnode, the original node
110871047Sjulian	 * is not decremented automatically so we do that manually.
110971047Sjulian	 */
111071047Sjulian	_NG_HOOK_NODE(hook) = &ng_deadnode;
111171047Sjulian	NG_NODE_UNREF(node);	/* We no longer point to it so adjust count */
111271047Sjulian	NG_HOOK_UNREF(hook);	/* Account for linkage (in list) to node */
111352419Sjulian}
111452419Sjulian
111552419Sjulian/*
111652419Sjulian * Take two hooks on a node and merge the connection so that the given node
111752419Sjulian * is effectively bypassed.
111852419Sjulian */
111952419Sjulianint
112052419Sjulianng_bypass(hook_p hook1, hook_p hook2)
112152419Sjulian{
112270784Sjulian	if (hook1->hk_node != hook2->hk_node) {
112371047Sjulian		TRAP_ERROR();
112452419Sjulian		return (EINVAL);
112570784Sjulian	}
112670784Sjulian	hook1->hk_peer->hk_peer = hook2->hk_peer;
112770784Sjulian	hook2->hk_peer->hk_peer = hook1->hk_peer;
112852419Sjulian
112970939Sjulian	hook1->hk_peer = &ng_deadhook;
113070939Sjulian	hook2->hk_peer = &ng_deadhook;
113170939Sjulian
1132163244Sglebius	NG_HOOK_UNREF(hook1);
1133163244Sglebius	NG_HOOK_UNREF(hook2);
1134163244Sglebius
113552419Sjulian	/* XXX If we ever cache methods on hooks update them as well */
113652419Sjulian	ng_destroy_hook(hook1);
113752419Sjulian	ng_destroy_hook(hook2);
113852419Sjulian	return (0);
113952419Sjulian}
114052419Sjulian
114152419Sjulian/*
114252419Sjulian * Install a new netgraph type
114352419Sjulian */
114452419Sjulianint
114552419Sjulianng_newtype(struct ng_type *tp)
114652419Sjulian{
114752419Sjulian	const size_t namelen = strlen(tp->name);
114852419Sjulian
114952419Sjulian	/* Check version and type name fields */
115070159Sjulian	if ((tp->version != NG_ABI_VERSION)
115170159Sjulian	|| (namelen == 0)
1152125028Sharti	|| (namelen >= NG_TYPESIZ)) {
115371047Sjulian		TRAP_ERROR();
1154131374Sjulian		if (tp->version != NG_ABI_VERSION) {
1155131374Sjulian			printf("Netgraph: Node type rejected. ABI mismatch. Suggest recompile\n");
1156131374Sjulian		}
115752419Sjulian		return (EINVAL);
115852419Sjulian	}
115952419Sjulian
116052419Sjulian	/* Check for name collision */
116152419Sjulian	if (ng_findtype(tp->name) != NULL) {
116271047Sjulian		TRAP_ERROR();
116352419Sjulian		return (EEXIST);
116452419Sjulian	}
116552419Sjulian
116670700Sjulian
116752419Sjulian	/* Link in new type */
116872200Sbmilekic	mtx_lock(&ng_typelist_mtx);
116970700Sjulian	LIST_INSERT_HEAD(&ng_typelist, tp, types);
117071603Sjulian	tp->refs = 1;	/* first ref is linked list */
117172200Sbmilekic	mtx_unlock(&ng_typelist_mtx);
117252419Sjulian	return (0);
117352419Sjulian}
117452419Sjulian
117552419Sjulian/*
117680222Sjulian * unlink a netgraph type
117780222Sjulian * If no examples exist
117880222Sjulian */
117980222Sjulianint
118080222Sjulianng_rmtype(struct ng_type *tp)
118180222Sjulian{
118280222Sjulian	/* Check for name collision */
118380222Sjulian	if (tp->refs != 1) {
118480222Sjulian		TRAP_ERROR();
118580222Sjulian		return (EBUSY);
118680222Sjulian	}
118780222Sjulian
118880222Sjulian	/* Unlink type */
118980222Sjulian	mtx_lock(&ng_typelist_mtx);
119080222Sjulian	LIST_REMOVE(tp, types);
119180222Sjulian	mtx_unlock(&ng_typelist_mtx);
119280222Sjulian	return (0);
119380222Sjulian}
119480222Sjulian
119580222Sjulian/*
119652419Sjulian * Look for a type of the name given
119752419Sjulian */
119852419Sjulianstruct ng_type *
119952419Sjulianng_findtype(const char *typename)
120052419Sjulian{
120152419Sjulian	struct ng_type *type;
120252419Sjulian
120372200Sbmilekic	mtx_lock(&ng_typelist_mtx);
120470700Sjulian	LIST_FOREACH(type, &ng_typelist, types) {
120552419Sjulian		if (strcmp(type->name, typename) == 0)
120652419Sjulian			break;
120752419Sjulian	}
120872200Sbmilekic	mtx_unlock(&ng_typelist_mtx);
120952419Sjulian	return (type);
121052419Sjulian}
121152419Sjulian
121252419Sjulian/************************************************************************
121352419Sjulian			Composite routines
121452419Sjulian************************************************************************/
121552419Sjulian/*
121671047Sjulian * Connect two nodes using the specified hooks, using queued functions.
121752419Sjulian */
121871849Sjulianstatic void
121971047Sjulianng_con_part3(node_p node, hook_p hook, void *arg1, int arg2)
122052419Sjulian{
122152419Sjulian
122271047Sjulian	/*
122371047Sjulian	 * When we run, we know that the node 'node' is locked for us.
122471047Sjulian	 * Our caller has a reference on the hook.
122571047Sjulian	 * Our caller has a reference on the node.
122671047Sjulian	 * (In this case our caller is ng_apply_item() ).
122771047Sjulian	 * The peer hook has a reference on the hook.
122871849Sjulian	 * We are all set up except for the final call to the node, and
122971849Sjulian	 * the clearing of the INVALID flag.
123071047Sjulian	 */
123171047Sjulian	if (NG_HOOK_NODE(hook) == &ng_deadnode) {
123271047Sjulian		/*
123371047Sjulian		 * The node must have been freed again since we last visited
123471047Sjulian		 * here. ng_destry_hook() has this effect but nothing else does.
123571047Sjulian		 * We should just release our references and
123671047Sjulian		 * free anything we can think of.
123771047Sjulian		 * Since we know it's been destroyed, and it's our caller
123871047Sjulian		 * that holds the references, just return.
123971047Sjulian		 */
124071849Sjulian		return ;
124152419Sjulian	}
124271047Sjulian	if (hook->hk_node->nd_type->connect) {
124371849Sjulian		if ((*hook->hk_node->nd_type->connect) (hook)) {
124471047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
124571849Sjulian			printf("failed in ng_con_part3()\n");
124671849Sjulian			return ;
124771047Sjulian		}
124852419Sjulian	}
124971047Sjulian	/*
125071047Sjulian	 *  XXX this is wrong for SMP. Possibly we need
125171047Sjulian	 * to separate out 'create' and 'invalid' flags.
125271047Sjulian	 * should only set flags on hooks we have locked under our node.
125371047Sjulian	 */
125471047Sjulian	hook->hk_flags &= ~HK_INVALID;
125571849Sjulian	return ;
125671047Sjulian}
125752419Sjulian
125871849Sjulianstatic void
125971047Sjulianng_con_part2(node_p node, hook_p hook, void *arg1, int arg2)
126071047Sjulian{
1261151974Sglebius	hook_p peer;
126271047Sjulian
126352419Sjulian	/*
126471047Sjulian	 * When we run, we know that the node 'node' is locked for us.
126571047Sjulian	 * Our caller has a reference on the hook.
126671047Sjulian	 * Our caller has a reference on the node.
126771047Sjulian	 * (In this case our caller is ng_apply_item() ).
126871047Sjulian	 * The peer hook has a reference on the hook.
126971047Sjulian	 * our node pointer points to the 'dead' node.
127071047Sjulian	 * First check the hook name is unique.
127171849Sjulian	 * Should not happen because we checked before queueing this.
127252419Sjulian	 */
127371047Sjulian	if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) {
127471047Sjulian		TRAP_ERROR();
127571047Sjulian		ng_destroy_hook(hook); /* should destroy peer too */
127671849Sjulian		printf("failed in ng_con_part2()\n");
127771849Sjulian		return ;
127871047Sjulian	}
127970939Sjulian	/*
128071047Sjulian	 * Check if the node type code has something to say about it
128171047Sjulian	 * If it fails, the unref of the hook will also unref the attached node,
128271047Sjulian	 * however since that node is 'ng_deadnode' this will do nothing.
128371047Sjulian	 * The peer hook will also be destroyed.
128470939Sjulian	 */
128571047Sjulian	if (node->nd_type->newhook != NULL) {
128671849Sjulian		if ((*node->nd_type->newhook)(node, hook, hook->hk_name)) {
128771047Sjulian			ng_destroy_hook(hook); /* should destroy peer too */
128871849Sjulian			printf("failed in ng_con_part2()\n");
128971849Sjulian			return ;
129071047Sjulian		}
129171047Sjulian	}
129271047Sjulian
129371047Sjulian	/*
129471047Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
129571047Sjulian	 * We'll ask again later when we actually connect the hooks.
129671047Sjulian	 */
129771047Sjulian	hook->hk_node = node;		/* just overwrite ng_deadnode */
129871047Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
129971047Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
130071047Sjulian	node->nd_numhooks++;
130171047Sjulian	NG_HOOK_REF(hook);	/* one for the node */
130271047Sjulian
130371047Sjulian	/*
130471047Sjulian	 * We now have a symetrical situation, where both hooks have been
130574078Sjulian	 * linked to their nodes, the newhook methods have been called
130671047Sjulian	 * And the references are all correct. The hooks are still marked
130771047Sjulian	 * as invalid, as we have not called the 'connect' methods
130871047Sjulian	 * yet.
1309152451Sglebius	 * We can call the local one immediatly as we have the
131071047Sjulian	 * node locked, but we need to queue the remote one.
131171047Sjulian	 */
131271047Sjulian	if (hook->hk_node->nd_type->connect) {
131371849Sjulian		if ((*hook->hk_node->nd_type->connect) (hook)) {
131471047Sjulian			ng_destroy_hook(hook);	/* also zaps peer */
131571849Sjulian			printf("failed in ng_con_part2(A)\n");
131671849Sjulian			return ;
131771047Sjulian		}
131871047Sjulian	}
1319151974Sglebius
1320151974Sglebius	/*
1321151974Sglebius	 * Acquire topo mutex to avoid race with ng_destroy_hook().
1322151974Sglebius	 */
1323151974Sglebius	mtx_lock(&ng_topo_mtx);
1324151974Sglebius	peer = hook->hk_peer;
1325151974Sglebius	if (peer == &ng_deadhook) {
1326151974Sglebius		mtx_unlock(&ng_topo_mtx);
1327151974Sglebius		printf("failed in ng_con_part2(B)\n");
1328151974Sglebius		ng_destroy_hook(hook);
1329151974Sglebius		return ;
1330151974Sglebius	}
1331151974Sglebius	mtx_unlock(&ng_topo_mtx);
1332151974Sglebius
1333151974Sglebius	if (ng_send_fn(peer->hk_node, peer, &ng_con_part3, arg1, arg2)) {
1334151974Sglebius		printf("failed in ng_con_part2(C)\n");
133571849Sjulian		ng_destroy_hook(hook);	/* also zaps peer */
133671849Sjulian		return ;
133771849Sjulian	}
133871047Sjulian	hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */
133971849Sjulian	return ;
134052419Sjulian}
134152419Sjulian
134252419Sjulian/*
1343152451Sglebius * Connect this node with another node. We assume that this node is
134471047Sjulian * currently locked, as we are only called from an NGM_CONNECT message.
134552419Sjulian */
134671047Sjulianstatic int
134752419Sjulianng_con_nodes(node_p node, const char *name, node_p node2, const char *name2)
134852419Sjulian{
1349152451Sglebius	int	error;
1350152451Sglebius	hook_p	hook;
1351152451Sglebius	hook_p	hook2;
135252419Sjulian
135371849Sjulian	if (ng_findhook(node2, name2) != NULL) {
135471849Sjulian		return(EEXIST);
135571849Sjulian	}
135670939Sjulian	if ((error = ng_add_hook(node, name, &hook)))  /* gives us a ref */
135752419Sjulian		return (error);
135871047Sjulian	/* Allocate the other hook and link it up */
135971047Sjulian	NG_ALLOC_HOOK(hook2);
1360140737Sglebius	if (hook2 == NULL) {
136171047Sjulian		TRAP_ERROR();
136271047Sjulian		ng_destroy_hook(hook);	/* XXX check ref counts so far */
136371047Sjulian		NG_HOOK_UNREF(hook);	/* including our ref */
136471047Sjulian		return (ENOMEM);
136571047Sjulian	}
136671047Sjulian	hook2->hk_refs = 1;		/* start with a reference for us. */
136771047Sjulian	hook2->hk_flags = HK_INVALID;
136871047Sjulian	hook2->hk_peer = hook;		/* Link the two together */
136971047Sjulian	hook->hk_peer = hook2;
137071047Sjulian	NG_HOOK_REF(hook);		/* Add a ref for the peer to each*/
137171047Sjulian	NG_HOOK_REF(hook2);
1372152451Sglebius	hook2->hk_node = &ng_deadnode;
1373125028Sharti	strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ);
137471047Sjulian
137571047Sjulian	/*
137671047Sjulian	 * Queue the function above.
137771047Sjulian	 * Procesing continues in that function in the lock context of
137871047Sjulian	 * the other node.
137971047Sjulian	 */
138071849Sjulian	ng_send_fn(node2, hook2, &ng_con_part2, NULL, 0);
138171047Sjulian
138271047Sjulian	NG_HOOK_UNREF(hook);		/* Let each hook go if it wants to */
138371047Sjulian	NG_HOOK_UNREF(hook2);
138471849Sjulian	return (0);
138571047Sjulian}
138671047Sjulian
138771047Sjulian/*
138871047Sjulian * Make a peer and connect.
138971047Sjulian * We assume that the local node is locked.
139071047Sjulian * The new node probably doesn't need a lock until
139171047Sjulian * it has a hook, because it cannot really have any work until then,
139271047Sjulian * but we should think about it a bit more.
139371047Sjulian *
139471047Sjulian * The problem may come if the other node also fires up
139571047Sjulian * some hardware or a timer or some other source of activation,
139671047Sjulian * also it may already get a command msg via it's ID.
139771047Sjulian *
139871047Sjulian * We could use the same method as ng_con_nodes() but we'd have
1399152451Sglebius * to add ability to remove the node when failing. (Not hard, just
140071047Sjulian * make arg1 point to the node to remove).
140171047Sjulian * Unless of course we just ignore failure to connect and leave
140271047Sjulian * an unconnected node?
140371047Sjulian */
140471047Sjulianstatic int
140571047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type)
140671047Sjulian{
1407152451Sglebius	node_p	node2;
1408152451Sglebius	hook_p	hook1, hook2;
1409152451Sglebius	int	error;
141071047Sjulian
141171047Sjulian	if ((error = ng_make_node(type, &node2))) {
141252419Sjulian		return (error);
141352419Sjulian	}
141470939Sjulian
141571047Sjulian	if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */
141671849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
141771047Sjulian		return (error);
141871047Sjulian	}
141971047Sjulian
142071047Sjulian	if ((error = ng_add_hook(node2, name2, &hook2))) {
142171849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
142271047Sjulian		ng_destroy_hook(hook1);
142371047Sjulian		NG_HOOK_UNREF(hook1);
142471047Sjulian		return (error);
142571047Sjulian	}
142671047Sjulian
142770939Sjulian	/*
142871047Sjulian	 * Actually link the two hooks together.
142971047Sjulian	 */
143071047Sjulian	hook1->hk_peer = hook2;
143171047Sjulian	hook2->hk_peer = hook1;
143271047Sjulian
143371047Sjulian	/* Each hook is referenced by the other */
143471047Sjulian	NG_HOOK_REF(hook1);
143571047Sjulian	NG_HOOK_REF(hook2);
143671047Sjulian
143771047Sjulian	/* Give each node the opportunity to veto the pending connection */
143871047Sjulian	if (hook1->hk_node->nd_type->connect) {
143971047Sjulian		error = (*hook1->hk_node->nd_type->connect) (hook1);
144071047Sjulian	}
144171047Sjulian
144271047Sjulian	if ((error == 0) && hook2->hk_node->nd_type->connect) {
144371047Sjulian		error = (*hook2->hk_node->nd_type->connect) (hook2);
144471047Sjulian
144571047Sjulian	}
144671047Sjulian
144771047Sjulian	/*
144870939Sjulian	 * drop the references we were holding on the two hooks.
144970939Sjulian	 */
145071047Sjulian	if (error) {
145171047Sjulian		ng_destroy_hook(hook2);	/* also zaps hook1 */
145271849Sjulian		ng_rmnode(node2, NULL, NULL, 0);
145371047Sjulian	} else {
145471047Sjulian		/* As a last act, allow the hooks to be used */
145571047Sjulian		hook1->hk_flags &= ~HK_INVALID;
145671047Sjulian		hook2->hk_flags &= ~HK_INVALID;
145771047Sjulian	}
145871047Sjulian	NG_HOOK_UNREF(hook1);
145970939Sjulian	NG_HOOK_UNREF(hook2);
146070939Sjulian	return (error);
146152419Sjulian}
146271047Sjulian
146370700Sjulian/************************************************************************
146470700Sjulian		Utility routines to send self messages
146570700Sjulian************************************************************************/
146670700Sjulian
146771849Sjulian/* Shut this node down as soon as everyone is clear of it */
146871849Sjulian/* Should add arg "immediatly" to jump the queue */
146970700Sjulianint
147071849Sjulianng_rmnode_self(node_p node)
147170700Sjulian{
147271849Sjulian	int		error;
147352419Sjulian
147471849Sjulian	if (node == &ng_deadnode)
147571849Sjulian		return (0);
1476132464Sjulian	node->nd_flags |= NGF_INVALID;
1477132464Sjulian	if (node->nd_flags & NGF_CLOSING)
147871849Sjulian		return (0);
147970700Sjulian
148071849Sjulian	error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0);
148171849Sjulian	return (error);
148270700Sjulian}
148370700Sjulian
148471849Sjulianstatic void
148571047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2)
148671047Sjulian{
148771047Sjulian	ng_destroy_hook(hook);
148871849Sjulian	return ;
148971047Sjulian}
149071047Sjulian
149170935Sjulianint
149270935Sjulianng_rmhook_self(hook_p hook)
149370935Sjulian{
149471047Sjulian	int		error;
149570935Sjulian	node_p node = NG_HOOK_NODE(hook);
149670935Sjulian
149771047Sjulian	if (node == &ng_deadnode)
149871047Sjulian		return (0);
149971047Sjulian
150071047Sjulian	error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0);
150171047Sjulian	return (error);
150270935Sjulian}
150370935Sjulian
150470700Sjulian/***********************************************************************
150552419Sjulian * Parse and verify a string of the form:  <NODE:><PATH>
150652419Sjulian *
150752419Sjulian * Such a string can refer to a specific node or a specific hook
150852419Sjulian * on a specific node, depending on how you look at it. In the
150952419Sjulian * latter case, the PATH component must not end in a dot.
151052419Sjulian *
151152419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string
151252419Sjulian * of hook names separated by dots. This breaks out the original
151352419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp
151452419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to
151552419Sjulian * the final hook component of <PATH>, if any, otherwise NULL.
151652419Sjulian *
151752419Sjulian * This returns -1 if the path is malformed. The char ** are optional.
151870700Sjulian ***********************************************************************/
151952419Sjulianint
152052419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
152152419Sjulian{
1522152451Sglebius	char	*node, *path, *hook;
1523152451Sglebius	int	k;
152452419Sjulian
152552419Sjulian	/*
152652419Sjulian	 * Extract absolute NODE, if any
152752419Sjulian	 */
152852419Sjulian	for (path = addr; *path && *path != ':'; path++);
152952419Sjulian	if (*path) {
153052419Sjulian		node = addr;	/* Here's the NODE */
153152419Sjulian		*path++ = '\0';	/* Here's the PATH */
153252419Sjulian
153352419Sjulian		/* Node name must not be empty */
153452419Sjulian		if (!*node)
153552419Sjulian			return -1;
153652419Sjulian
153752419Sjulian		/* A name of "." is OK; otherwise '.' not allowed */
153852419Sjulian		if (strcmp(node, ".") != 0) {
153952419Sjulian			for (k = 0; node[k]; k++)
154052419Sjulian				if (node[k] == '.')
154152419Sjulian					return -1;
154252419Sjulian		}
154352419Sjulian	} else {
154452419Sjulian		node = NULL;	/* No absolute NODE */
154552419Sjulian		path = addr;	/* Here's the PATH */
154652419Sjulian	}
154752419Sjulian
154852419Sjulian	/* Snoop for illegal characters in PATH */
154952419Sjulian	for (k = 0; path[k]; k++)
155052419Sjulian		if (path[k] == ':')
155152419Sjulian			return -1;
155252419Sjulian
155352419Sjulian	/* Check for no repeated dots in PATH */
155452419Sjulian	for (k = 0; path[k]; k++)
155552419Sjulian		if (path[k] == '.' && path[k + 1] == '.')
155652419Sjulian			return -1;
155752419Sjulian
155852419Sjulian	/* Remove extra (degenerate) dots from beginning or end of PATH */
155952419Sjulian	if (path[0] == '.')
156052419Sjulian		path++;
156152419Sjulian	if (*path && path[strlen(path) - 1] == '.')
156252419Sjulian		path[strlen(path) - 1] = 0;
156352419Sjulian
156452419Sjulian	/* If PATH has a dot, then we're not talking about a hook */
156552419Sjulian	if (*path) {
156652419Sjulian		for (hook = path, k = 0; path[k]; k++)
156752419Sjulian			if (path[k] == '.') {
156852419Sjulian				hook = NULL;
156952419Sjulian				break;
157052419Sjulian			}
157152419Sjulian	} else
157252419Sjulian		path = hook = NULL;
157352419Sjulian
157452419Sjulian	/* Done */
157552419Sjulian	if (nodep)
157652419Sjulian		*nodep = node;
157752419Sjulian	if (pathp)
157852419Sjulian		*pathp = path;
157952419Sjulian	if (hookp)
158052419Sjulian		*hookp = hook;
158152419Sjulian	return (0);
158252419Sjulian}
158352419Sjulian
158452419Sjulian/*
158552419Sjulian * Given a path, which may be absolute or relative, and a starting node,
158670700Sjulian * return the destination node.
158752419Sjulian */
158852419Sjulianint
158970700Sjulianng_path2noderef(node_p here, const char *address,
159070700Sjulian				node_p *destp, hook_p *lasthook)
159152419Sjulian{
1592125028Sharti	char    fullpath[NG_PATHSIZ];
159352419Sjulian	char   *nodename, *path, pbuf[2];
159470700Sjulian	node_p  node, oldnode;
159552419Sjulian	char   *cp;
159659728Sjulian	hook_p hook = NULL;
159752419Sjulian
159852419Sjulian	/* Initialize */
159970784Sjulian	if (destp == NULL) {
160071047Sjulian		TRAP_ERROR();
160152419Sjulian		return EINVAL;
160270784Sjulian	}
160352419Sjulian	*destp = NULL;
160452419Sjulian
160552419Sjulian	/* Make a writable copy of address for ng_path_parse() */
160652419Sjulian	strncpy(fullpath, address, sizeof(fullpath) - 1);
160752419Sjulian	fullpath[sizeof(fullpath) - 1] = '\0';
160852419Sjulian
160952419Sjulian	/* Parse out node and sequence of hooks */
161052419Sjulian	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
161171047Sjulian		TRAP_ERROR();
161252419Sjulian		return EINVAL;
161352419Sjulian	}
161452419Sjulian	if (path == NULL) {
161552419Sjulian		pbuf[0] = '.';	/* Needs to be writable */
161652419Sjulian		pbuf[1] = '\0';
161752419Sjulian		path = pbuf;
161852419Sjulian	}
161952419Sjulian
162070700Sjulian	/*
162170700Sjulian	 * For an absolute address, jump to the starting node.
162270700Sjulian	 * Note that this holds a reference on the node for us.
162370700Sjulian	 * Don't forget to drop the reference if we don't need it.
162470700Sjulian	 */
162552419Sjulian	if (nodename) {
162670700Sjulian		node = ng_name2noderef(here, nodename);
162752419Sjulian		if (node == NULL) {
162871047Sjulian			TRAP_ERROR();
162952419Sjulian			return (ENOENT);
163052419Sjulian		}
163170700Sjulian	} else {
163270700Sjulian		if (here == NULL) {
163371047Sjulian			TRAP_ERROR();
163470700Sjulian			return (EINVAL);
163570700Sjulian		}
163652419Sjulian		node = here;
163770784Sjulian		NG_NODE_REF(node);
163870700Sjulian	}
163952419Sjulian
164070700Sjulian	/*
1641152451Sglebius	 * Now follow the sequence of hooks
164270700Sjulian	 * XXX
164370700Sjulian	 * We actually cannot guarantee that the sequence
164470700Sjulian	 * is not being demolished as we crawl along it
164570700Sjulian	 * without extra-ordinary locking etc.
164670700Sjulian	 * So this is a bit dodgy to say the least.
164770700Sjulian	 * We can probably hold up some things by holding
164870700Sjulian	 * the nodelist mutex for the time of this
164970700Sjulian	 * crawl if we wanted.. At least that way we wouldn't have to
165070700Sjulian	 * worry about the nodes dissappearing, but the hooks would still
165170700Sjulian	 * be a problem.
165270700Sjulian	 */
165352419Sjulian	for (cp = path; node != NULL && *cp != '\0'; ) {
165452419Sjulian		char *segment;
165552419Sjulian
165652419Sjulian		/*
165752419Sjulian		 * Break out the next path segment. Replace the dot we just
165852419Sjulian		 * found with a NUL; "cp" points to the next segment (or the
165952419Sjulian		 * NUL at the end).
166052419Sjulian		 */
166152419Sjulian		for (segment = cp; *cp != '\0'; cp++) {
166252419Sjulian			if (*cp == '.') {
166352419Sjulian				*cp++ = '\0';
166452419Sjulian				break;
166552419Sjulian			}
166652419Sjulian		}
166752419Sjulian
166852419Sjulian		/* Empty segment */
166952419Sjulian		if (*segment == '\0')
167052419Sjulian			continue;
167152419Sjulian
167252419Sjulian		/* We have a segment, so look for a hook by that name */
167354096Sarchie		hook = ng_findhook(node, segment);
167452419Sjulian
167552419Sjulian		/* Can't get there from here... */
167652419Sjulian		if (hook == NULL
167770784Sjulian		    || NG_HOOK_PEER(hook) == NULL
167870784Sjulian		    || NG_HOOK_NOT_VALID(hook)
167970784Sjulian		    || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
168071047Sjulian			TRAP_ERROR();
168170784Sjulian			NG_NODE_UNREF(node);
1682152451Sglebius#if 0
168370784Sjulian			printf("hooknotvalid %s %s %d %d %d %d ",
168470784Sjulian					path,
168570784Sjulian					segment,
168670784Sjulian					hook == NULL,
1687152451Sglebius					NG_HOOK_PEER(hook) == NULL,
1688152451Sglebius					NG_HOOK_NOT_VALID(hook),
1689152451Sglebius					NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)));
169070784Sjulian#endif
169152419Sjulian			return (ENOENT);
169252419Sjulian		}
169352419Sjulian
169470700Sjulian		/*
1695152451Sglebius		 * Hop on over to the next node
169670700Sjulian		 * XXX
1697152451Sglebius		 * Big race conditions here as hooks and nodes go away
169870700Sjulian		 * *** Idea.. store an ng_ID_t in each hook and use that
169970700Sjulian		 * instead of the direct hook in this crawl?
170070700Sjulian		 */
170170700Sjulian		oldnode = node;
170270784Sjulian		if ((node = NG_PEER_NODE(hook)))
170370784Sjulian			NG_NODE_REF(node);	/* XXX RACE */
170470784Sjulian		NG_NODE_UNREF(oldnode);	/* XXX another race */
170570784Sjulian		if (NG_NODE_NOT_VALID(node)) {
170670784Sjulian			NG_NODE_UNREF(node);	/* XXX more races */
170770700Sjulian			node = NULL;
170870700Sjulian		}
170952419Sjulian	}
171052419Sjulian
171152419Sjulian	/* If node somehow missing, fail here (probably this is not needed) */
171252419Sjulian	if (node == NULL) {
171371047Sjulian		TRAP_ERROR();
171452419Sjulian		return (ENXIO);
171552419Sjulian	}
171652419Sjulian
171752419Sjulian	/* Done */
171852419Sjulian	*destp = node;
171959900Sarchie	if (lasthook != NULL)
172070784Sjulian		*lasthook = (hook ? NG_HOOK_PEER(hook) : NULL);
172152419Sjulian	return (0);
172252419Sjulian}
172352419Sjulian
172470700Sjulian/***************************************************************\
172570700Sjulian* Input queue handling.
172670700Sjulian* All activities are submitted to the node via the input queue
172770700Sjulian* which implements a multiple-reader/single-writer gate.
172870700Sjulian* Items which cannot be handled immeditly are queued.
172970700Sjulian*
173070700Sjulian* read-write queue locking inline functions			*
173170700Sjulian\***************************************************************/
173270700Sjulian
1733151238Sglebiusstatic __inline item_p ng_dequeue(struct ng_queue * ngq, int *rw);
173470700Sjulianstatic __inline item_p ng_acquire_read(struct ng_queue * ngq,
173570700Sjulian					item_p  item);
173670700Sjulianstatic __inline item_p ng_acquire_write(struct ng_queue * ngq,
173770700Sjulian					item_p  item);
173870700Sjulianstatic __inline void	ng_leave_read(struct ng_queue * ngq);
173970700Sjulianstatic __inline void	ng_leave_write(struct ng_queue * ngq);
174070700Sjulianstatic __inline void	ng_queue_rw(struct ng_queue * ngq,
174170700Sjulian					item_p  item, int rw);
174270700Sjulian
174352419Sjulian/*
174470700Sjulian * Definition of the bits fields in the ng_queue flag word.
174570700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle
174670700Sjulian * with them.
174770700Sjulian *
174871902Sjulian * The ordering here may be important! don't shuffle these.
174952419Sjulian */
175070700Sjulian/*-
175170700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet)
175270700Sjulian                       |
175370700Sjulian                       V
175470700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
1755151973Sglebius  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
1756151973Sglebius  | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A|
1757151973Sglebius  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W|
175870700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
1759151973Sglebius  \___________________________ ____________________________/ | |
1760151973Sglebius                            V                                | |
1761151973Sglebius                  [active reader count]                      | |
176270700Sjulian                                                             | |
1763151973Sglebius            Operation Pending -------------------------------+ |
176470700Sjulian                                                               |
1765151973Sglebius          Active Writer ---------------------------------------+
176671902Sjulian
176771902Sjulian
176870700Sjulian*/
1769151973Sglebius#define WRITER_ACTIVE	0x00000001
1770151973Sglebius#define OP_PENDING	0x00000002
1771151973Sglebius#define READER_INCREMENT 0x00000004
1772151973Sglebius#define READER_MASK	0xfffffffc	/* Not valid if WRITER_ACTIVE is set */
1773151973Sglebius#define SAFETY_BARRIER	0x00100000	/* 128K items queued should be enough */
177471902Sjulian
177571902Sjulian/* Defines of more elaborate states on the queue */
1776151973Sglebius/* Mask of bits a new read cares about */
1777151973Sglebius#define NGQ_RMASK	(WRITER_ACTIVE|OP_PENDING)
177871902Sjulian
1779151973Sglebius/* Mask of bits a new write cares about */
178071902Sjulian#define NGQ_WMASK	(NGQ_RMASK|READER_MASK)
178171902Sjulian
1782151973Sglebius/* Test to decide if there is something on the queue. */
1783151973Sglebius#define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING)
178471902Sjulian
1785151973Sglebius/* How to decide what the next queued item is. */
1786151973Sglebius#define HEAD_IS_READER(QP)  NGI_QUEUED_READER((QP)->queue)
1787151973Sglebius#define HEAD_IS_WRITER(QP)  NGI_QUEUED_WRITER((QP)->queue) /* notused */
1788151973Sglebius
1789151973Sglebius/* Read the status to decide if the next item on the queue can now run. */
1790151973Sglebius#define QUEUED_READER_CAN_PROCEED(QP)			\
1791151973Sglebius		(((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0)
1792151973Sglebius#define QUEUED_WRITER_CAN_PROCEED(QP)			\
1793151973Sglebius		(((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0)
1794151973Sglebius
179571902Sjulian/* Is there a chance of getting ANY work off the queue? */
1796151973Sglebius#define NEXT_QUEUED_ITEM_CAN_PROCEED(QP)				\
1797151973Sglebius	(QUEUE_ACTIVE(QP) && 						\
1798151973Sglebius	((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) :		\
1799151973Sglebius				QUEUED_WRITER_CAN_PROCEED(QP)))
180071902Sjulian
1801151973Sglebius
1802151238Sglebius#define NGQRW_R 0
1803151238Sglebius#define NGQRW_W 1
1804151238Sglebius
180570700Sjulian/*
180670700Sjulian * Taking into account the current state of the queue and node, possibly take
180770700Sjulian * the next entry off the queue and return it. Return NULL if there was
180870700Sjulian * nothing we could return, either because there really was nothing there, or
180970700Sjulian * because the node was in a state where it cannot yet process the next item
181070700Sjulian * on the queue.
181170700Sjulian *
181270700Sjulian * This MUST MUST MUST be called with the mutex held.
181370700Sjulian */
181470700Sjulianstatic __inline item_p
1815151238Sglebiusng_dequeue(struct ng_queue *ngq, int *rw)
181670700Sjulian{
181770700Sjulian	item_p item;
181870700Sjulian	u_int		add_arg;
181971902Sjulian
1820139039Sglebius	mtx_assert(&ngq->q_mtx, MA_OWNED);
1821152451Sglebius	/*
1822151973Sglebius	 * If there is nothing queued, then just return.
1823151973Sglebius	 * No point in continuing.
1824151973Sglebius	 * XXXGL: assert this?
1825151973Sglebius	 */
1826151973Sglebius	if (!QUEUE_ACTIVE(ngq)) {
1827154275Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; "
1828154275Sglebius		    "queue flags 0x%lx", __func__,
1829154275Sglebius		    ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags);
1830151973Sglebius		return (NULL);
1831151973Sglebius	}
1832139039Sglebius
1833151973Sglebius	/*
1834151973Sglebius	 * From here, we can assume there is a head item.
1835151973Sglebius	 * We need to find out what it is and if it can be dequeued, given
1836151973Sglebius	 * the current state of the node.
1837151973Sglebius	 */
1838151973Sglebius	if (HEAD_IS_READER(ngq)) {
1839151973Sglebius		if (!QUEUED_READER_CAN_PROCEED(ngq)) {
1840151973Sglebius			/*
1841151973Sglebius			 * It's a reader but we can't use it.
1842151973Sglebius			 * We are stalled so make sure we don't
1843151973Sglebius			 * get called again until something changes.
1844151973Sglebius			 */
1845151973Sglebius			ng_worklist_remove(ngq->q_node);
1846154275Sglebius			CTR4(KTR_NET, "%20s: node [%x] (%p) queued reader "
1847154275Sglebius			    "can't proceed; queue flags 0x%lx", __func__,
1848154275Sglebius			    ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags);
1849151973Sglebius			return (NULL);
1850151973Sglebius		}
185170700Sjulian		/*
185271902Sjulian		 * Head of queue is a reader and we have no write active.
1853152451Sglebius		 * We don't care how many readers are already active.
1854151973Sglebius		 * Add the correct increment for the reader count.
185570700Sjulian		 */
1856151973Sglebius		add_arg = READER_INCREMENT;
1857151238Sglebius		*rw = NGQRW_R;
1858151973Sglebius	} else if (QUEUED_WRITER_CAN_PROCEED(ngq)) {
185970700Sjulian		/*
186070700Sjulian		 * There is a pending write, no readers and no active writer.
186170700Sjulian		 * This means we can go ahead with the pending writer. Note
186270700Sjulian		 * the fact that we now have a writer, ready for when we take
186370700Sjulian		 * it off the queue.
186470700Sjulian		 *
186570700Sjulian		 * We don't need to worry about a possible collision with the
186670700Sjulian		 * fasttrack reader.
186770700Sjulian		 *
186870700Sjulian		 * The fasttrack thread may take a long time to discover that we
186970700Sjulian		 * are running so we would have an inconsistent state in the
187070700Sjulian		 * flags for a while. Since we ignore the reader count
187170700Sjulian		 * entirely when the WRITER_ACTIVE flag is set, this should
187270700Sjulian		 * not matter (in fact it is defined that way). If it tests
1873151973Sglebius		 * the flag before this operation, the OP_PENDING flag
187470700Sjulian		 * will make it fail, and if it tests it later, the
187571902Sjulian		 * WRITER_ACTIVE flag will do the same. If it is SO slow that
187670700Sjulian		 * we have actually completed the operation, and neither flag
1877151973Sglebius		 * is set by the time that it tests the flags, then it is
1878151973Sglebius		 * actually ok for it to continue. If it completes and we've
1879151973Sglebius		 * finished and the read pending is set it still fails.
188070700Sjulian		 *
188170700Sjulian		 * So we can just ignore it,  as long as we can ensure that the
188270700Sjulian		 * transition from WRITE_PENDING state to the WRITER_ACTIVE
188370700Sjulian		 * state is atomic.
188470700Sjulian		 *
188570700Sjulian		 * After failing, first it will be held back by the mutex, then
188670700Sjulian		 * when it can proceed, it will queue its request, then it
188770700Sjulian		 * would arrive at this function. Usually it will have to
188871902Sjulian		 * leave empty handed because the ACTIVE WRITER bit will be
188970700Sjulian		 * set.
189071902Sjulian		 *
1891151973Sglebius		 * Adjust the flags for the new active writer.
189270700Sjulian		 */
1893151973Sglebius		add_arg = WRITER_ACTIVE;
1894151238Sglebius		*rw = NGQRW_W;
189570700Sjulian		/*
189670700Sjulian		 * We want to write "active writer, no readers " Now go make
189770700Sjulian		 * it true. In fact there may be a number in the readers
189870700Sjulian		 * count but we know it is not true and will be fixed soon.
189970700Sjulian		 * We will fix the flags for the next pending entry in a
190070700Sjulian		 * moment.
190170700Sjulian		 */
190270700Sjulian	} else {
190370700Sjulian		/*
190470700Sjulian		 * We can't dequeue anything.. return and say so. Probably we
190570700Sjulian		 * have a write pending and the readers count is non zero. If
190670700Sjulian		 * we got here because a reader hit us just at the wrong
190770700Sjulian		 * moment with the fasttrack code, and put us in a strange
1908151973Sglebius		 * state, then it will be coming through in just a moment,
1909151973Sglebius		 * (just as soon as we release the mutex) and keep things
1910151973Sglebius		 * moving.
1911151973Sglebius		 * Make sure we remove ourselves from the work queue. It
1912151973Sglebius		 * would be a waste of effort to do all this again.
191370700Sjulian		 */
191471902Sjulian		ng_worklist_remove(ngq->q_node);
1915154275Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) can't dequeue anything; "
1916154275Sglebius		    "queue flags 0x%lx", __func__,
1917154275Sglebius		    ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags);
1918151973Sglebius		return (NULL);
191970700Sjulian	}
192052419Sjulian
192170700Sjulian	/*
192270700Sjulian	 * Now we dequeue the request (whatever it may be) and correct the
192370700Sjulian	 * pending flags and the next and last pointers.
192470700Sjulian	 */
192570700Sjulian	item = ngq->queue;
192670700Sjulian	ngq->queue = item->el_next;
1927154275Sglebius	CTR6(KTR_NET, "%20s: node [%x] (%p) dequeued item %p with flags 0x%lx; "
1928154275Sglebius	    "queue flags 0x%lx", __func__,
1929154275Sglebius	    ngq->q_node->nd_ID,ngq->q_node, item, item->el_flags, ngq->q_flags);
193070700Sjulian	if (ngq->last == &(item->el_next)) {
193170700Sjulian		/*
193270700Sjulian		 * that was the last entry in the queue so set the 'last
1933151973Sglebius		 * pointer up correctly and make sure the pending flag is
193470700Sjulian		 * clear.
193570700Sjulian		 */
1936151973Sglebius		add_arg += -OP_PENDING;
193770700Sjulian		ngq->last = &(ngq->queue);
193870700Sjulian		/*
193971902Sjulian		 * Whatever flag was set will be cleared and
194071902Sjulian		 * the new acive field will be set by the add as well,
194171902Sjulian		 * so we don't need to change add_arg.
194271902Sjulian		 * But we know we don't need to be on the work list.
194370700Sjulian		 */
194471902Sjulian		atomic_add_long(&ngq->q_flags, add_arg);
194571902Sjulian		ng_worklist_remove(ngq->q_node);
194670700Sjulian	} else {
1947152451Sglebius		/*
1948151973Sglebius		 * Since there is still something on the queue
1949151973Sglebius		 * we don't need to change the PENDING flag.
195071902Sjulian		 */
195171902Sjulian		atomic_add_long(&ngq->q_flags, add_arg);
195271902Sjulian		/*
195371902Sjulian		 * If we see more doable work, make sure we are
195471902Sjulian		 * on the work queue.
195571902Sjulian		 */
1956151973Sglebius		if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) {
195771902Sjulian			ng_setisr(ngq->q_node);
195871902Sjulian		}
195970700Sjulian	}
1960154275Sglebius	CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; "
1961154275Sglebius	    "queue flags 0x%lx", __func__,
1962154275Sglebius	    ngq->q_node->nd_ID, ngq->q_node, item, *rw ? "WRITER" : "READER" ,
1963154225Sglebius	    ngq->q_flags);
196470700Sjulian	return (item);
196570700Sjulian}
196652419Sjulian
196770700Sjulian/*
196870700Sjulian * Queue a packet to be picked up by someone else.
196970700Sjulian * We really don't care who, but we can't or don't want to hang around
197070700Sjulian * to process it ourselves. We are probably an interrupt routine..
1971151973Sglebius * If the queue could be run, flag the netisr handler to start.
197270700Sjulian */
197370700Sjulianstatic __inline void
197470700Sjulianng_queue_rw(struct ng_queue * ngq, item_p  item, int rw)
197570700Sjulian{
1976139039Sglebius	mtx_assert(&ngq->q_mtx, MA_OWNED);
1977139039Sglebius
1978151973Sglebius	if (rw == NGQRW_W)
1979151973Sglebius		NGI_SET_WRITER(item);
1980151973Sglebius	else
1981151973Sglebius		NGI_SET_READER(item);
198270700Sjulian	item->el_next = NULL;	/* maybe not needed */
198370700Sjulian	*ngq->last = item;
1984154275Sglebius	CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__,
1985154275Sglebius	    ngq->q_node->nd_ID, ngq->q_node, item, rw ? "WRITER" : "READER" );
198670700Sjulian	/*
198770700Sjulian	 * If it was the first item in the queue then we need to
198870700Sjulian	 * set the last pointer and the type flags.
198970700Sjulian	 */
1990154225Sglebius	if (ngq->last == &(ngq->queue)) {
1991151973Sglebius		atomic_add_long(&ngq->q_flags, OP_PENDING);
1992154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
1993154275Sglebius		    ngq->q_node->nd_ID, ngq->q_node);
1994154225Sglebius	}
1995151973Sglebius
199670700Sjulian	ngq->last = &(item->el_next);
1997151973Sglebius	/*
1998151973Sglebius	 * We can take the worklist lock with the node locked
1999151973Sglebius	 * BUT NOT THE REVERSE!
2000151973Sglebius	 */
2001151973Sglebius	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2002151973Sglebius		ng_setisr(ngq->q_node);
200370700Sjulian}
200452419Sjulian
200570700Sjulian
200652419Sjulian/*
200770700Sjulian * This function 'cheats' in that it first tries to 'grab' the use of the
200870700Sjulian * node, without going through the mutex. We can do this becasue of the
200970700Sjulian * semantics of the lock. The semantics include a clause that says that the
201070700Sjulian * value of the readers count is invalid if the WRITER_ACTIVE flag is set. It
201170700Sjulian * also says that the WRITER_ACTIVE flag cannot be set if the readers count
201270700Sjulian * is not zero. Note that this talks about what is valid to SET the
201370700Sjulian * WRITER_ACTIVE flag, because from the moment it is set, the value if the
201470700Sjulian * reader count is immaterial, and not valid. The two 'pending' flags have a
201570700Sjulian * similar effect, in that If they are orthogonal to the two active fields in
201670700Sjulian * how they are set, but if either is set, the attempted 'grab' need to be
201770700Sjulian * backed out because there is earlier work, and we maintain ordering in the
201870700Sjulian * queue. The result of this is that the reader request can try obtain use of
201970700Sjulian * the node with only a single atomic addition, and without any of the mutex
202070700Sjulian * overhead. If this fails the operation degenerates to the same as for other
202170700Sjulian * cases.
202270700Sjulian *
202352419Sjulian */
202470700Sjulianstatic __inline item_p
202570700Sjulianng_acquire_read(struct ng_queue *ngq, item_p item)
202652419Sjulian{
2027151974Sglebius	KASSERT(ngq != &ng_deadnode.nd_input_queue,
2028151974Sglebius	    ("%s: working on deadnode", __func__));
202952419Sjulian
203070700Sjulian	/* ######### Hack alert ######### */
203170700Sjulian	atomic_add_long(&ngq->q_flags, READER_INCREMENT);
203271902Sjulian	if ((ngq->q_flags & NGQ_RMASK) == 0) {
203370700Sjulian		/* Successfully grabbed node */
2034154275Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) fast acquired item %p",
2035154275Sglebius		    __func__, ngq->q_node->nd_ID, ngq->q_node, item);
203670700Sjulian		return (item);
203770700Sjulian	}
203870700Sjulian	/* undo the damage if we didn't succeed */
203970700Sjulian	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
204070700Sjulian
204170700Sjulian	/* ######### End Hack alert ######### */
204272200Sbmilekic	mtx_lock_spin((&ngq->q_mtx));
204369922Sjulian	/*
204470700Sjulian	 * Try again. Another processor (or interrupt for that matter) may
204570700Sjulian	 * have removed the last queued item that was stopping us from
204670700Sjulian	 * running, between the previous test, and the moment that we took
204770700Sjulian	 * the mutex. (Or maybe a writer completed.)
2048151973Sglebius	 * Even if another fast-track reader hits during this period
2049151973Sglebius	 * we don't care as multiple readers is OK.
205069922Sjulian	 */
205171902Sjulian	if ((ngq->q_flags & NGQ_RMASK) == 0) {
205270700Sjulian		atomic_add_long(&ngq->q_flags, READER_INCREMENT);
205372200Sbmilekic		mtx_unlock_spin((&ngq->q_mtx));
2054154275Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) slow acquired item %p",
2055154275Sglebius		    __func__, ngq->q_node->nd_ID, ngq->q_node, item);
205670700Sjulian		return (item);
205770700Sjulian	}
205870700Sjulian
205970700Sjulian	/*
206070700Sjulian	 * and queue the request for later.
206170700Sjulian	 */
206270700Sjulian	ng_queue_rw(ngq, item, NGQRW_R);
2063148236Sglebius	mtx_unlock_spin(&(ngq->q_mtx));
206470700Sjulian
2065148236Sglebius	return (NULL);
206670700Sjulian}
206770700Sjulian
206870700Sjulianstatic __inline item_p
206970700Sjulianng_acquire_write(struct ng_queue *ngq, item_p item)
207070700Sjulian{
2071151974Sglebius	KASSERT(ngq != &ng_deadnode.nd_input_queue,
2072151974Sglebius	    ("%s: working on deadnode", __func__));
2073151974Sglebius
207470700Sjulianrestart:
207572200Sbmilekic	mtx_lock_spin(&(ngq->q_mtx));
207670700Sjulian	/*
207770700Sjulian	 * If there are no readers, no writer, and no pending packets, then
207870700Sjulian	 * we can just go ahead. In all other situations we need to queue the
207970700Sjulian	 * request
208070700Sjulian	 */
208171902Sjulian	if ((ngq->q_flags & NGQ_WMASK) == 0) {
2082151973Sglebius		/* collision could happen *HERE* */
208370700Sjulian		atomic_add_long(&ngq->q_flags, WRITER_ACTIVE);
208472200Sbmilekic		mtx_unlock_spin((&ngq->q_mtx));
208570700Sjulian		if (ngq->q_flags & READER_MASK) {
208670700Sjulian			/* Collision with fast-track reader */
208771902Sjulian			atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE);
208870700Sjulian			goto restart;
208969922Sjulian		}
2090154275Sglebius		CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p",
2091154275Sglebius		    __func__, ngq->q_node->nd_ID, ngq->q_node, item);
209270700Sjulian		return (item);
209352419Sjulian	}
209452419Sjulian
209570700Sjulian	/*
209670700Sjulian	 * and queue the request for later.
209770700Sjulian	 */
209870700Sjulian	ng_queue_rw(ngq, item, NGQRW_W);
2099148236Sglebius	mtx_unlock_spin(&(ngq->q_mtx));
210070700Sjulian
2101148236Sglebius	return (NULL);
210270700Sjulian}
210370700Sjulian
2104167385Sjulian#if 0
2105167385Sjulianstatic __inline item_p
2106167385Sjulianng_upgrade_write(struct ng_queue *ngq, item_p item)
2107167385Sjulian{
2108167385Sjulian	KASSERT(ngq != &ng_deadnode.nd_input_queue,
2109167385Sjulian	    ("%s: working on deadnode", __func__));
2110167385Sjulian
2111167385Sjulian	NGI_SET_WRITER(item);
2112167385Sjulian
2113167385Sjulian	mtx_lock_spin(&(ngq->q_mtx));
2114167385Sjulian
2115167385Sjulian	/*
2116167385Sjulian	 * There will never be no readers as we are there ourselves.
2117167385Sjulian	 * Set the WRITER_ACTIVE flags ASAP to block out fast track readers.
2118167385Sjulian	 * The caller we are running from will call ng_leave_read()
2119167385Sjulian	 * soon, so we must account for that. We must leave again with the
2120167385Sjulian	 * READER lock. If we find other readers, then
2121167385Sjulian	 * queue the request for later. However "later" may be rignt now
2122167385Sjulian	 * if there are no readers. We don't really care if there are queued
2123167385Sjulian	 * items as we will bypass them anyhow.
2124167385Sjulian	 */
2125167385Sjulian	atomic_add_long(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT);
2126167385Sjulian	if (ngq->q_flags & (NGQ_WMASK & ~OP_PENDING) == WRITER_ACTIVE) {
2127167385Sjulian		mtx_unlock_spin(&(ngq->q_mtx));
2128167385Sjulian
2129167385Sjulian		/* It's just us, act on the item. */
2130167385Sjulian		/* will NOT drop writer lock when done */
2131167385Sjulian		ng_apply_item(node, item, 0);
2132167385Sjulian
2133167385Sjulian		/*
2134167385Sjulian		 * Having acted on the item, atomically
2135167385Sjulian		 * down grade back to READER and finish up
2136167385Sjulian	 	 */
2137167385Sjulian		atomic_add_long(&ngq->q_flags,
2138167385Sjulian		    READER_INCREMENT - WRITER_ACTIVE);
2139167385Sjulian
2140167385Sjulian		/* Our caller will call ng_leave_read() */
2141167385Sjulian		return;
2142167385Sjulian	}
2143167385Sjulian	/*
2144167385Sjulian	 * It's not just us active, so queue us AT THE HEAD.
2145167385Sjulian	 * "Why?" I hear you ask.
2146167385Sjulian	 * Put us at the head of the queue as we've already been
2147167385Sjulian	 * through it once. If there is nothing else waiting,
2148167385Sjulian	 * set the correct flags.
2149167385Sjulian	 */
2150167385Sjulian	if ((item->el_next = ngq->queue) == NULL) {
2151167385Sjulian		/*
2152167385Sjulian		 * Set up the "last" pointer.
2153167385Sjulian		 * We are the only (and thus last) item
2154167385Sjulian		 */
2155167385Sjulian		ngq->last = &(item->el_next);
2156167385Sjulian
2157167385Sjulian		/* We've gone from, 0 to 1 item in the queue */
2158167385Sjulian		atomic_add_long(&ngq->q_flags, OP_PENDING);
2159167385Sjulian
2160167385Sjulian		CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__,
2161167385Sjulian		    ngq->q_node->nd_ID, ngq->q_node);
2162167385Sjulian	};
2163167385Sjulian	ngq->queue = item;
2164167385Sjulian	CTR5(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER",
2165167385Sjulian	    __func__, ngq->q_node->nd_ID, ngq->q_node, item );
2166167385Sjulian
2167167385Sjulian	/* Reverse what we did above. That downgrades us back to reader */
2168167385Sjulian	atomic_add_long(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE);
2169167385Sjulian	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2170167385Sjulian		ng_setisr(ngq->q_node);
2171167385Sjulian	mtx_unlock_spin(&(ngq->q_mtx));
2172167385Sjulian
2173167385Sjulian	return;
2174167385Sjulian}
2175167385Sjulian
2176167385Sjulian#endif
2177167385Sjulian
217870700Sjulianstatic __inline void
217970700Sjulianng_leave_read(struct ng_queue *ngq)
218070700Sjulian{
218170700Sjulian	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
218270700Sjulian}
218370700Sjulian
218470700Sjulianstatic __inline void
218570700Sjulianng_leave_write(struct ng_queue *ngq)
218670700Sjulian{
218770700Sjulian	atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE);
218870700Sjulian}
218970700Sjulian
219070700Sjulianstatic void
219170700Sjulianng_flush_input_queue(struct ng_queue * ngq)
219270700Sjulian{
219370700Sjulian	item_p item;
2194151973Sglebius
219572200Sbmilekic	mtx_lock_spin(&ngq->q_mtx);
2196151973Sglebius	while (ngq->queue) {
219770700Sjulian		item = ngq->queue;
219870700Sjulian		ngq->queue = item->el_next;
219970700Sjulian		if (ngq->last == &(item->el_next)) {
220070700Sjulian			ngq->last = &(ngq->queue);
2201151973Sglebius			atomic_add_long(&ngq->q_flags, -OP_PENDING);
220270700Sjulian		}
2203151973Sglebius		mtx_unlock_spin(&ngq->q_mtx);
220470700Sjulian
2205151973Sglebius		/* If the item is supplying a callback, call it with an error */
2206151283Sglebius		if (item->apply != NULL) {
2207151283Sglebius			(item->apply)(item->context, ENOENT);
2208151283Sglebius			item->apply = NULL;
2209151283Sglebius		}
2210132369Sjulian		NG_FREE_ITEM(item);
221172200Sbmilekic		mtx_lock_spin(&ngq->q_mtx);
221270700Sjulian	}
221371902Sjulian	/*
221471902Sjulian	 * Take us off the work queue if we are there.
221571902Sjulian	 * We definatly have no work to be done.
221671902Sjulian	 */
221771902Sjulian	ng_worklist_remove(ngq->q_node);
221872200Sbmilekic	mtx_unlock_spin(&ngq->q_mtx);
221970700Sjulian}
222070700Sjulian
222170700Sjulian/***********************************************************************
222270700Sjulian* Externally visible method for sending or queueing messages or data.
222370700Sjulian***********************************************************************/
222470700Sjulian
222570700Sjulian/*
222671849Sjulian * The module code should have filled out the item correctly by this stage:
222770700Sjulian * Common:
222870700Sjulian *    reference to destination node.
222970700Sjulian *    Reference to destination rcv hook if relevant.
223070700Sjulian * Data:
223170700Sjulian *    pointer to mbuf
223270700Sjulian * Control_Message:
223370700Sjulian *    pointer to msg.
223470700Sjulian *    ID of original sender node. (return address)
223571849Sjulian * Function:
223671849Sjulian *    Function pointer
223771849Sjulian *    void * argument
223871849Sjulian *    integer argument
223970700Sjulian *
224070700Sjulian * The nodes have several routines and macros to help with this task:
224170700Sjulian */
224270700Sjulian
224370700Sjulianint
2244146281Sglebiusng_snd_item(item_p item, int flags)
224570700Sjulian{
224671849Sjulian	hook_p hook = NGI_HOOK(item);
224771902Sjulian	node_p node = NGI_NODE(item);
2248146281Sglebius	int queue, rw;
224971902Sjulian	struct ng_queue * ngq = &node->nd_input_queue;
2250151256Sglebius	int error = 0;
225170700Sjulian
225270784Sjulian#ifdef	NETGRAPH_DEBUG
2253152451Sglebius	_ngi_check(item, __FILE__, __LINE__);
225470700Sjulian#endif
225570700Sjulian
2256146281Sglebius	queue = (flags & NG_QUEUE) ? 1 : 0;
2257146281Sglebius
225870700Sjulian	if (item == NULL) {
225971047Sjulian		TRAP_ERROR();
226070700Sjulian		return (EINVAL);	/* failed to get queue element */
226170700Sjulian	}
226271902Sjulian	if (node == NULL) {
226370700Sjulian		NG_FREE_ITEM(item);
226471047Sjulian		TRAP_ERROR();
226570700Sjulian		return (EINVAL);	/* No address */
226670700Sjulian	}
226771047Sjulian	switch(item->el_flags & NGQF_TYPE) {
226871047Sjulian	case NGQF_DATA:
226969922Sjulian		/*
227070700Sjulian		 * DATA MESSAGE
227170700Sjulian		 * Delivered to a node via a non-optional hook.
227270700Sjulian		 * Both should be present in the item even though
227370700Sjulian		 * the node is derivable from the hook.
227470700Sjulian		 * References are held on both by the item.
227569922Sjulian		 */
2276131112Sjulian
2277131112Sjulian		/* Protect nodes from sending NULL pointers
2278131112Sjulian		 * to each other
2279131112Sjulian		 */
2280131123Sjulian		if (NGI_M(item) == NULL)
2281131112Sjulian			return (EINVAL);
2282131112Sjulian
228370700Sjulian		CHECK_DATA_MBUF(NGI_M(item));
228470700Sjulian		if (hook == NULL) {
228570700Sjulian			NG_FREE_ITEM(item);
228671047Sjulian			TRAP_ERROR();
228770700Sjulian			return(EINVAL);
228870700Sjulian		}
228970784Sjulian		if ((NG_HOOK_NOT_VALID(hook))
229070784Sjulian		|| (NG_NODE_NOT_VALID(NG_HOOK_NODE(hook)))) {
229170700Sjulian			NG_FREE_ITEM(item);
229270700Sjulian			return (ENOTCONN);
229369922Sjulian		}
229470784Sjulian		if ((hook->hk_flags & HK_QUEUE)) {
229570700Sjulian			queue = 1;
229670700Sjulian		}
229771047Sjulian		break;
229871047Sjulian	case NGQF_MESG:
229970700Sjulian		/*
230070700Sjulian		 * CONTROL MESSAGE
230170700Sjulian		 * Delivered to a node.
230270700Sjulian		 * Hook is optional.
230370700Sjulian		 * References are held by the item on the node and
230470700Sjulian		 * the hook if it is present.
230570700Sjulian		 */
230670784Sjulian		if (hook && (hook->hk_flags & HK_QUEUE)) {
230770700Sjulian			queue = 1;
230870700Sjulian		}
230971047Sjulian		break;
231071047Sjulian	case NGQF_FN:
231171047Sjulian		break;
231271047Sjulian	default:
231371047Sjulian		NG_FREE_ITEM(item);
231471047Sjulian		TRAP_ERROR();
231571047Sjulian		return (EINVAL);
231669922Sjulian	}
2317149505Sglebius	switch(item->el_flags & NGQF_RW) {
2318149505Sglebius	case NGQF_READER:
2319149505Sglebius		rw = NGQRW_R;
2320149505Sglebius		break;
2321149505Sglebius	case NGQF_WRITER:
2322149505Sglebius		rw = NGQRW_W;
2323149505Sglebius		break;
2324149505Sglebius	default:
2325149505Sglebius		panic("%s: invalid item flags %lx", __func__, item->el_flags);
2326149505Sglebius	}
2327149505Sglebius
232870700Sjulian	/*
2329149505Sglebius	 * If the node specifies single threading, force writer semantics.
2330149505Sglebius	 * Similarly, the node may say one hook always produces writers.
2331151238Sglebius	 * These are overrides.
233270700Sjulian	 */
2333132464Sjulian	if ((node->nd_flags & NGF_FORCE_WRITER)
2334151238Sglebius	    || (hook && (hook->hk_flags & HK_FORCE_WRITER)))
233570700Sjulian			rw = NGQRW_W;
2336149505Sglebius
233770700Sjulian	if (queue) {
233870700Sjulian		/* Put it on the queue for that node*/
233970784Sjulian#ifdef	NETGRAPH_DEBUG
2340152451Sglebius		_ngi_check(item, __FILE__, __LINE__);
234170700Sjulian#endif
234272200Sbmilekic		mtx_lock_spin(&(ngq->q_mtx));
234370700Sjulian		ng_queue_rw(ngq, item, rw);
234472200Sbmilekic		mtx_unlock_spin(&(ngq->q_mtx));
2345147774Sglebius
2346147774Sglebius		if (flags & NG_PROGRESS)
2347147774Sglebius			return (EINPROGRESS);
2348147774Sglebius		else
2349147774Sglebius			return (0);
235070700Sjulian	}
235169922Sjulian
235270700Sjulian	/*
235370700Sjulian	 * We already decided how we will be queueud or treated.
235470700Sjulian	 * Try get the appropriate operating permission.
235570700Sjulian	 */
2356151256Sglebius 	if (rw == NGQRW_R)
235770700Sjulian		item = ng_acquire_read(ngq, item);
2358151256Sglebius	else
235970700Sjulian		item = ng_acquire_write(ngq, item);
236052419Sjulian
2361151973Sglebius
236270700Sjulian	if (item == NULL) {
2363147774Sglebius		if (flags & NG_PROGRESS)
2364147774Sglebius			return (EINPROGRESS);
2365147774Sglebius		else
2366147774Sglebius			return (0);
236770700Sjulian	}
236852419Sjulian
236970784Sjulian#ifdef	NETGRAPH_DEBUG
2370152451Sglebius	_ngi_check(item, __FILE__, __LINE__);
237170700Sjulian#endif
2372151973Sglebius
237374078Sjulian	NGI_GET_NODE(item, node); /* zaps stored node */
237452419Sjulian
2375167385Sjulian	/* Don't report any errors. act as if it had been queued */
2376167385Sjulian	ng_apply_item(node, item, rw); /* drops r/w lock when done */
237774078Sjulian
237852419Sjulian	/*
2379152451Sglebius	 * If the node goes away when we remove the reference,
238082058Sbrian	 * whatever we just did caused it.. whatever we do, DO NOT
238174078Sjulian	 * access the node again!
238274078Sjulian	 */
238374078Sjulian	if (NG_NODE_UNREF(node) == 0) {
238474078Sjulian		return (error);
238574078Sjulian	}
238674078Sjulian
2387148236Sglebius	mtx_lock_spin(&(ngq->q_mtx));
2388151973Sglebius	if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq))
2389148236Sglebius		ng_setisr(ngq->q_node);
2390148236Sglebius	mtx_unlock_spin(&(ngq->q_mtx));
239170700Sjulian
239271849Sjulian	return (error);
239352419Sjulian}
239452419Sjulian
239552419Sjulian/*
239670700Sjulian * We have an item that was possibly queued somewhere.
239770700Sjulian * It should contain all the information needed
239870700Sjulian * to run it on the appropriate node/hook.
239952419Sjulian */
2400167385Sjulianstatic void
2401151238Sglebiusng_apply_item(node_p node, item_p item, int rw)
240252419Sjulian{
240370700Sjulian	hook_p  hook;
240470700Sjulian	ng_rcvdata_t *rcvdata;
240571885Sjulian	ng_rcvmsg_t *rcvmsg;
2406147774Sglebius	ng_apply_t *apply = NULL;
2407147774Sglebius	void	*context = NULL;
240852419Sjulian
240971849Sjulian	NGI_GET_HOOK(item, hook); /* clears stored hook */
241070784Sjulian#ifdef	NETGRAPH_DEBUG
2411152451Sglebius	_ngi_check(item, __FILE__, __LINE__);
241270700Sjulian#endif
2413147774Sglebius
2414147774Sglebius	/*
2415151973Sglebius	 * If the item has an "apply" callback, store it.
2416151973Sglebius	 * Clear item's callback immediately, to avoid an extra call if
2417151973Sglebius	 * the item is reused by the destination node.
2418147774Sglebius	 */
2419147774Sglebius	if (item->apply != NULL) {
2420147774Sglebius		apply = item->apply;
2421147774Sglebius		context = item->context;
2422147774Sglebius		item->apply = NULL;
2423147774Sglebius	}
2424147774Sglebius
242571047Sjulian	switch (item->el_flags & NGQF_TYPE) {
242670700Sjulian	case NGQF_DATA:
242770700Sjulian		/*
242870700Sjulian		 * Check things are still ok as when we were queued.
242970700Sjulian		 */
243070700Sjulian		if ((hook == NULL)
243170784Sjulian		|| NG_HOOK_NOT_VALID(hook)
243271885Sjulian		|| NG_NODE_NOT_VALID(node) ) {
243370700Sjulian			NG_FREE_ITEM(item);
243471885Sjulian			break;
243570700Sjulian		}
243671885Sjulian		/*
243771885Sjulian		 * If no receive method, just silently drop it.
243871885Sjulian		 * Give preference to the hook over-ride method
243971885Sjulian		 */
2440152451Sglebius		if ((!(rcvdata = hook->hk_rcvdata))
244171885Sjulian		&& (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) {
244271885Sjulian			error = 0;
244371885Sjulian			NG_FREE_ITEM(item);
244471885Sjulian			break;
244571885Sjulian		}
2446167385Sjulian		(*rcvdata)(hook, item);
244770700Sjulian		break;
244870700Sjulian	case NGQF_MESG:
244970700Sjulian		if (hook) {
245070784Sjulian			if (NG_HOOK_NOT_VALID(hook)) {
245171849Sjulian				/*
245271849Sjulian				 * The hook has been zapped then we can't
245371849Sjulian				 * use it. Immediatly drop its reference.
245471849Sjulian				 * The message may not need it.
245571849Sjulian				 */
245670784Sjulian				NG_HOOK_UNREF(hook);
245770700Sjulian				hook = NULL;
245870700Sjulian			}
245970700Sjulian		}
246070700Sjulian		/*
246170700Sjulian		 * Similarly, if the node is a zombie there is
246270700Sjulian		 * nothing we can do with it, drop everything.
246370700Sjulian		 */
246470784Sjulian		if (NG_NODE_NOT_VALID(node)) {
246571047Sjulian			TRAP_ERROR();
246670700Sjulian			NG_FREE_ITEM(item);
246770700Sjulian		} else {
246870700Sjulian			/*
246970700Sjulian			 * Call the appropriate message handler for the object.
247070700Sjulian			 * It is up to the message handler to free the message.
247170700Sjulian			 * If it's a generic message, handle it generically,
247270700Sjulian			 * otherwise call the type's message handler
247370700Sjulian			 * (if it exists)
247470700Sjulian			 * XXX (race). Remember that a queued message may
247570700Sjulian			 * reference a node or hook that has just been
247670700Sjulian			 * invalidated. It will exist as the queue code
247770700Sjulian			 * is holding a reference, but..
247870700Sjulian			 */
247970700Sjulian
248070700Sjulian			struct ng_mesg *msg = NGI_MSG(item);
248170700Sjulian
2482152451Sglebius			/*
248371885Sjulian			 * check if the generic handler owns it.
248471885Sjulian			 */
248570700Sjulian			if ((msg->header.typecookie == NGM_GENERIC_COOKIE)
248670700Sjulian			&& ((msg->header.flags & NGF_RESP) == 0)) {
2487167385Sjulian				ng_generic_msg(node, item, hook);
248871885Sjulian				break;
248970700Sjulian			}
249071885Sjulian			/*
249171885Sjulian			 * Now see if there is a handler (hook or node specific)
249271885Sjulian			 * in the target node. If none, silently discard.
249371885Sjulian			 */
249471885Sjulian			if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg)))
249571885Sjulian			&& (!(rcvmsg = node->nd_type->rcvmsg))) {
249671885Sjulian				TRAP_ERROR();
249771885Sjulian				NG_FREE_ITEM(item);
249871885Sjulian				break;
249971885Sjulian			}
2500167385Sjulian			(*rcvmsg)(node, item, hook);
250170700Sjulian		}
250270700Sjulian		break;
250371047Sjulian	case NGQF_FN:
250471047Sjulian		/*
250571047Sjulian		 *  We have to implicitly trust the hook,
250671047Sjulian		 * as some of these are used for system purposes
250771849Sjulian		 * where the hook is invalid. In the case of
250871849Sjulian		 * the shutdown message we allow it to hit
250971849Sjulian		 * even if the node is invalid.
251071047Sjulian		 */
2511152451Sglebius		if ((NG_NODE_NOT_VALID(node))
251271849Sjulian		&& (NGI_FN(item) != &ng_rmnode)) {
251371047Sjulian			TRAP_ERROR();
2514143384Sglebius			NG_FREE_ITEM(item);
251571047Sjulian			break;
251671047Sjulian		}
251771849Sjulian		(*NGI_FN(item))(node, hook, NGI_ARG1(item), NGI_ARG2(item));
251871047Sjulian		NG_FREE_ITEM(item);
251971047Sjulian		break;
252071047Sjulian
252170700Sjulian	}
252270700Sjulian	/*
252370700Sjulian	 * We held references on some of the resources
252470700Sjulian	 * that we took from the item. Now that we have
252570700Sjulian	 * finished doing everything, drop those references.
252670700Sjulian	 */
252770700Sjulian	if (hook) {
252870784Sjulian		NG_HOOK_UNREF(hook);
252970700Sjulian	}
253070700Sjulian
2531151238Sglebius 	if (rw == NGQRW_R) {
253270784Sjulian		ng_leave_read(&node->nd_input_queue);
2533167385Sjulian	} else if (rw == NGQRW_W) {
253470784Sjulian		ng_leave_write(&node->nd_input_queue);
2535167385Sjulian	} /* else do nothing */
2536147774Sglebius
2537167385Sjulian
2538147774Sglebius	/* Apply callback. */
2539147774Sglebius	if (apply != NULL)
2540147774Sglebius		(*apply)(context, error);
2541147774Sglebius
2542167385Sjulian	return;
254370700Sjulian}
254470700Sjulian
254570700Sjulian/***********************************************************************
254670700Sjulian * Implement the 'generic' control messages
254770700Sjulian ***********************************************************************/
254870700Sjulianstatic int
254970700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook)
255070700Sjulian{
255170700Sjulian	int error = 0;
255270700Sjulian	struct ng_mesg *msg;
255370700Sjulian	struct ng_mesg *resp = NULL;
255470700Sjulian
255570700Sjulian	NGI_GET_MSG(item, msg);
255652419Sjulian	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
255771047Sjulian		TRAP_ERROR();
255870700Sjulian		error = EINVAL;
255970700Sjulian		goto out;
256052419Sjulian	}
256152419Sjulian	switch (msg->header.cmd) {
256252419Sjulian	case NGM_SHUTDOWN:
256371849Sjulian		ng_rmnode(here, NULL, NULL, 0);
256452419Sjulian		break;
256552419Sjulian	case NGM_MKPEER:
256652419Sjulian	    {
256752419Sjulian		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
256852419Sjulian
256952419Sjulian		if (msg->header.arglen != sizeof(*mkp)) {
257071047Sjulian			TRAP_ERROR();
257170700Sjulian			error = EINVAL;
257270700Sjulian			break;
257352419Sjulian		}
257452419Sjulian		mkp->type[sizeof(mkp->type) - 1] = '\0';
257552419Sjulian		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
257652419Sjulian		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
257752419Sjulian		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
257852419Sjulian		break;
257952419Sjulian	    }
258052419Sjulian	case NGM_CONNECT:
258152419Sjulian	    {
258252419Sjulian		struct ngm_connect *const con =
258352419Sjulian			(struct ngm_connect *) msg->data;
258452419Sjulian		node_p node2;
258552419Sjulian
258652419Sjulian		if (msg->header.arglen != sizeof(*con)) {
258771047Sjulian			TRAP_ERROR();
258870700Sjulian			error = EINVAL;
258970700Sjulian			break;
259052419Sjulian		}
259152419Sjulian		con->path[sizeof(con->path) - 1] = '\0';
259252419Sjulian		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
259352419Sjulian		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
259470700Sjulian		/* Don't forget we get a reference.. */
259570700Sjulian		error = ng_path2noderef(here, con->path, &node2, NULL);
259652419Sjulian		if (error)
259752419Sjulian			break;
259852419Sjulian		error = ng_con_nodes(here, con->ourhook, node2, con->peerhook);
259970784Sjulian		NG_NODE_UNREF(node2);
260052419Sjulian		break;
260152419Sjulian	    }
260252419Sjulian	case NGM_NAME:
260352419Sjulian	    {
260452419Sjulian		struct ngm_name *const nam = (struct ngm_name *) msg->data;
260552419Sjulian
260652419Sjulian		if (msg->header.arglen != sizeof(*nam)) {
260771047Sjulian			TRAP_ERROR();
260870700Sjulian			error = EINVAL;
260970700Sjulian			break;
261052419Sjulian		}
261152419Sjulian		nam->name[sizeof(nam->name) - 1] = '\0';
261252419Sjulian		error = ng_name_node(here, nam->name);
261352419Sjulian		break;
261452419Sjulian	    }
261552419Sjulian	case NGM_RMHOOK:
261652419Sjulian	    {
261752419Sjulian		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
261852419Sjulian		hook_p hook;
261952419Sjulian
262052419Sjulian		if (msg->header.arglen != sizeof(*rmh)) {
262171047Sjulian			TRAP_ERROR();
262270700Sjulian			error = EINVAL;
262370700Sjulian			break;
262452419Sjulian		}
262552419Sjulian		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
262654096Sarchie		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
262752419Sjulian			ng_destroy_hook(hook);
262852419Sjulian		break;
262952419Sjulian	    }
263052419Sjulian	case NGM_NODEINFO:
263152419Sjulian	    {
263252419Sjulian		struct nodeinfo *ni;
263352419Sjulian
263470700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
263552419Sjulian		if (resp == NULL) {
263652419Sjulian			error = ENOMEM;
263752419Sjulian			break;
263852419Sjulian		}
263952419Sjulian
264052419Sjulian		/* Fill in node info */
264170700Sjulian		ni = (struct nodeinfo *) resp->data;
264270784Sjulian		if (NG_NODE_HAS_NAME(here))
2643125028Sharti			strcpy(ni->name, NG_NODE_NAME(here));
2644125028Sharti		strcpy(ni->type, here->nd_type->name);
264552722Sjulian		ni->id = ng_node2ID(here);
264670784Sjulian		ni->hooks = here->nd_numhooks;
264752419Sjulian		break;
264852419Sjulian	    }
264952419Sjulian	case NGM_LISTHOOKS:
265052419Sjulian	    {
265170784Sjulian		const int nhooks = here->nd_numhooks;
265252419Sjulian		struct hooklist *hl;
265352419Sjulian		struct nodeinfo *ni;
265452419Sjulian		hook_p hook;
265552419Sjulian
265652419Sjulian		/* Get response struct */
265770700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*hl)
265870700Sjulian		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
265952419Sjulian		if (resp == NULL) {
266052419Sjulian			error = ENOMEM;
266152419Sjulian			break;
266252419Sjulian		}
266370700Sjulian		hl = (struct hooklist *) resp->data;
266452419Sjulian		ni = &hl->nodeinfo;
266552419Sjulian
266652419Sjulian		/* Fill in node info */
266770784Sjulian		if (NG_NODE_HAS_NAME(here))
2668125028Sharti			strcpy(ni->name, NG_NODE_NAME(here));
2669125028Sharti		strcpy(ni->type, here->nd_type->name);
267052722Sjulian		ni->id = ng_node2ID(here);
267152419Sjulian
267252419Sjulian		/* Cycle through the linked list of hooks */
267352419Sjulian		ni->hooks = 0;
267470784Sjulian		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
267552419Sjulian			struct linkinfo *const link = &hl->link[ni->hooks];
267652419Sjulian
267752419Sjulian			if (ni->hooks >= nhooks) {
267852419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
267987599Sobrien				    __func__, "hooks");
268052419Sjulian				break;
268152419Sjulian			}
268270784Sjulian			if (NG_HOOK_NOT_VALID(hook))
268352419Sjulian				continue;
2684125028Sharti			strcpy(link->ourhook, NG_HOOK_NAME(hook));
2685125028Sharti			strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook));
268670784Sjulian			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
2687125028Sharti				strcpy(link->nodeinfo.name,
2688125028Sharti				    NG_PEER_NODE_NAME(hook));
2689125028Sharti			strcpy(link->nodeinfo.type,
2690125028Sharti			   NG_PEER_NODE(hook)->nd_type->name);
269170784Sjulian			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
269270784Sjulian			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
269352419Sjulian			ni->hooks++;
269452419Sjulian		}
269552419Sjulian		break;
269652419Sjulian	    }
269752419Sjulian
269852419Sjulian	case NGM_LISTNAMES:
269952419Sjulian	case NGM_LISTNODES:
270052419Sjulian	    {
270152419Sjulian		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
270252419Sjulian		struct namelist *nl;
270352419Sjulian		node_p node;
270452419Sjulian		int num = 0;
270552419Sjulian
270672200Sbmilekic		mtx_lock(&ng_nodelist_mtx);
270752419Sjulian		/* Count number of nodes */
270870784Sjulian		LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
270970912Sjulian			if (NG_NODE_IS_VALID(node)
271070912Sjulian			&& (unnamed || NG_NODE_HAS_NAME(node))) {
271152419Sjulian				num++;
271270912Sjulian			}
271352419Sjulian		}
271472200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
271552419Sjulian
271652419Sjulian		/* Get response struct */
271770700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*nl)
271870700Sjulian		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
271952419Sjulian		if (resp == NULL) {
272052419Sjulian			error = ENOMEM;
272152419Sjulian			break;
272252419Sjulian		}
272370700Sjulian		nl = (struct namelist *) resp->data;
272452419Sjulian
272552419Sjulian		/* Cycle through the linked list of nodes */
272652419Sjulian		nl->numnames = 0;
272772200Sbmilekic		mtx_lock(&ng_nodelist_mtx);
272870784Sjulian		LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
272952419Sjulian			struct nodeinfo *const np = &nl->nodeinfo[nl->numnames];
273052419Sjulian
2731159373Sglebius			if (NG_NODE_NOT_VALID(node))
2732159373Sglebius				continue;
2733159373Sglebius			if (!unnamed && (! NG_NODE_HAS_NAME(node)))
2734159373Sglebius				continue;
273552419Sjulian			if (nl->numnames >= num) {
273652419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
273787599Sobrien				    __func__, "nodes");
273852419Sjulian				break;
273952419Sjulian			}
274070784Sjulian			if (NG_NODE_HAS_NAME(node))
2741125028Sharti				strcpy(np->name, NG_NODE_NAME(node));
2742125028Sharti			strcpy(np->type, node->nd_type->name);
274352722Sjulian			np->id = ng_node2ID(node);
274470784Sjulian			np->hooks = node->nd_numhooks;
274552419Sjulian			nl->numnames++;
274652419Sjulian		}
274772200Sbmilekic		mtx_unlock(&ng_nodelist_mtx);
274852419Sjulian		break;
274952419Sjulian	    }
275052419Sjulian
275152419Sjulian	case NGM_LISTTYPES:
275252419Sjulian	    {
275352419Sjulian		struct typelist *tl;
275452419Sjulian		struct ng_type *type;
275552419Sjulian		int num = 0;
275652419Sjulian
275772200Sbmilekic		mtx_lock(&ng_typelist_mtx);
275852419Sjulian		/* Count number of types */
275970912Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
276052419Sjulian			num++;
276170912Sjulian		}
276272200Sbmilekic		mtx_unlock(&ng_typelist_mtx);
276352419Sjulian
276452419Sjulian		/* Get response struct */
276570700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*tl)
276670700Sjulian		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
276752419Sjulian		if (resp == NULL) {
276852419Sjulian			error = ENOMEM;
276952419Sjulian			break;
277052419Sjulian		}
277170700Sjulian		tl = (struct typelist *) resp->data;
277252419Sjulian
277352419Sjulian		/* Cycle through the linked list of types */
277452419Sjulian		tl->numtypes = 0;
277572200Sbmilekic		mtx_lock(&ng_typelist_mtx);
277670700Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
277752419Sjulian			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
277852419Sjulian
277952419Sjulian			if (tl->numtypes >= num) {
278052419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
278187599Sobrien				    __func__, "types");
278252419Sjulian				break;
278352419Sjulian			}
2784125028Sharti			strcpy(tp->type_name, type->name);
278571603Sjulian			tp->numnodes = type->refs - 1; /* don't count list */
278652419Sjulian			tl->numtypes++;
278752419Sjulian		}
278872200Sbmilekic		mtx_unlock(&ng_typelist_mtx);
278952419Sjulian		break;
279052419Sjulian	    }
279152419Sjulian
279253913Sarchie	case NGM_BINARY2ASCII:
279353913Sarchie	    {
279464510Sarchie		int bufSize = 20 * 1024;	/* XXX hard coded constant */
279553913Sarchie		const struct ng_parse_type *argstype;
279653913Sarchie		const struct ng_cmdlist *c;
279770700Sjulian		struct ng_mesg *binary, *ascii;
279853913Sarchie
279953913Sarchie		/* Data area must contain a valid netgraph message */
280053913Sarchie		binary = (struct ng_mesg *)msg->data;
2801152451Sglebius		if (msg->header.arglen < sizeof(struct ng_mesg) ||
2802152451Sglebius		    (msg->header.arglen - sizeof(struct ng_mesg) <
2803152451Sglebius		    binary->header.arglen)) {
280471047Sjulian			TRAP_ERROR();
280553913Sarchie			error = EINVAL;
280653913Sarchie			break;
280753913Sarchie		}
280853913Sarchie
280970700Sjulian		/* Get a response message with lots of room */
281070700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
281170159Sjulian		if (resp == NULL) {
281253913Sarchie			error = ENOMEM;
281353913Sarchie			break;
281453913Sarchie		}
281570700Sjulian		ascii = (struct ng_mesg *)resp->data;
281653913Sarchie
281753913Sarchie		/* Copy binary message header to response message payload */
281853913Sarchie		bcopy(binary, ascii, sizeof(*binary));
281953913Sarchie
282053913Sarchie		/* Find command by matching typecookie and command number */
282170784Sjulian		for (c = here->nd_type->cmdlist;
282253913Sarchie		    c != NULL && c->name != NULL; c++) {
282353913Sarchie			if (binary->header.typecookie == c->cookie
282453913Sarchie			    && binary->header.cmd == c->cmd)
282553913Sarchie				break;
282653913Sarchie		}
282753913Sarchie		if (c == NULL || c->name == NULL) {
282853913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
282953913Sarchie				if (binary->header.typecookie == c->cookie
283053913Sarchie				    && binary->header.cmd == c->cmd)
283153913Sarchie					break;
283253913Sarchie			}
283353913Sarchie			if (c->name == NULL) {
283470700Sjulian				NG_FREE_MSG(resp);
283553913Sarchie				error = ENOSYS;
283653913Sarchie				break;
283753913Sarchie			}
283853913Sarchie		}
283953913Sarchie
284053913Sarchie		/* Convert command name to ASCII */
284153913Sarchie		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
284253913Sarchie		    "%s", c->name);
284353913Sarchie
284453913Sarchie		/* Convert command arguments to ASCII */
284553913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
284653913Sarchie		    c->respType : c->mesgType;
284770912Sjulian		if (argstype == NULL) {
284853913Sarchie			*ascii->data = '\0';
284970912Sjulian		} else {
285053913Sarchie			if ((error = ng_unparse(argstype,
285153913Sarchie			    (u_char *)binary->data,
285253913Sarchie			    ascii->data, bufSize)) != 0) {
285370700Sjulian				NG_FREE_MSG(resp);
285453913Sarchie				break;
285553913Sarchie			}
285653913Sarchie		}
285753913Sarchie
285853913Sarchie		/* Return the result as struct ng_mesg plus ASCII string */
285953913Sarchie		bufSize = strlen(ascii->data) + 1;
286053913Sarchie		ascii->header.arglen = bufSize;
286170700Sjulian		resp->header.arglen = sizeof(*ascii) + bufSize;
286253913Sarchie		break;
286353913Sarchie	    }
286453913Sarchie
286553913Sarchie	case NGM_ASCII2BINARY:
286653913Sarchie	    {
286753913Sarchie		int bufSize = 2000;	/* XXX hard coded constant */
286853913Sarchie		const struct ng_cmdlist *c;
286953913Sarchie		const struct ng_parse_type *argstype;
287070700Sjulian		struct ng_mesg *ascii, *binary;
287159178Sarchie		int off = 0;
287253913Sarchie
287353913Sarchie		/* Data area must contain at least a struct ng_mesg + '\0' */
287453913Sarchie		ascii = (struct ng_mesg *)msg->data;
2875152451Sglebius		if ((msg->header.arglen < sizeof(*ascii) + 1) ||
2876152451Sglebius		    (ascii->header.arglen < 1) ||
2877152451Sglebius		    (msg->header.arglen < sizeof(*ascii) +
2878152451Sglebius		    ascii->header.arglen)) {
287971047Sjulian			TRAP_ERROR();
288053913Sarchie			error = EINVAL;
288153913Sarchie			break;
288253913Sarchie		}
288353913Sarchie		ascii->data[ascii->header.arglen - 1] = '\0';
288453913Sarchie
288570700Sjulian		/* Get a response message with lots of room */
288670700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
288770159Sjulian		if (resp == NULL) {
288853913Sarchie			error = ENOMEM;
288953913Sarchie			break;
289053913Sarchie		}
289170700Sjulian		binary = (struct ng_mesg *)resp->data;
289253913Sarchie
289353913Sarchie		/* Copy ASCII message header to response message payload */
289453913Sarchie		bcopy(ascii, binary, sizeof(*ascii));
289553913Sarchie
289653913Sarchie		/* Find command by matching ASCII command string */
289770784Sjulian		for (c = here->nd_type->cmdlist;
289853913Sarchie		    c != NULL && c->name != NULL; c++) {
289953913Sarchie			if (strcmp(ascii->header.cmdstr, c->name) == 0)
290053913Sarchie				break;
290153913Sarchie		}
290253913Sarchie		if (c == NULL || c->name == NULL) {
290353913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
290453913Sarchie				if (strcmp(ascii->header.cmdstr, c->name) == 0)
290553913Sarchie					break;
290653913Sarchie			}
290753913Sarchie			if (c->name == NULL) {
290870700Sjulian				NG_FREE_MSG(resp);
290953913Sarchie				error = ENOSYS;
291053913Sarchie				break;
291153913Sarchie			}
291253913Sarchie		}
291353913Sarchie
291453913Sarchie		/* Convert command name to binary */
291553913Sarchie		binary->header.cmd = c->cmd;
291653913Sarchie		binary->header.typecookie = c->cookie;
291753913Sarchie
291853913Sarchie		/* Convert command arguments to binary */
291953913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
292053913Sarchie		    c->respType : c->mesgType;
292170912Sjulian		if (argstype == NULL) {
292253913Sarchie			bufSize = 0;
292370912Sjulian		} else {
292453913Sarchie			if ((error = ng_parse(argstype, ascii->data,
292553913Sarchie			    &off, (u_char *)binary->data, &bufSize)) != 0) {
292670700Sjulian				NG_FREE_MSG(resp);
292753913Sarchie				break;
292853913Sarchie			}
292953913Sarchie		}
293053913Sarchie
293153913Sarchie		/* Return the result */
293253913Sarchie		binary->header.arglen = bufSize;
293370700Sjulian		resp->header.arglen = sizeof(*binary) + bufSize;
293453913Sarchie		break;
293553913Sarchie	    }
293653913Sarchie
293762471Sphk	case NGM_TEXT_CONFIG:
293852419Sjulian	case NGM_TEXT_STATUS:
293952419Sjulian		/*
294052419Sjulian		 * This one is tricky as it passes the command down to the
294152419Sjulian		 * actual node, even though it is a generic type command.
294270700Sjulian		 * This means we must assume that the item/msg is already freed
294352419Sjulian		 * when control passes back to us.
294452419Sjulian		 */
294570784Sjulian		if (here->nd_type->rcvmsg != NULL) {
294670700Sjulian			NGI_MSG(item) = msg; /* put it back as we found it */
294770784Sjulian			return((*here->nd_type->rcvmsg)(here, item, lasthook));
294852419Sjulian		}
294952419Sjulian		/* Fall through if rcvmsg not supported */
295052419Sjulian	default:
295171047Sjulian		TRAP_ERROR();
295252419Sjulian		error = EINVAL;
295352419Sjulian	}
295470700Sjulian	/*
295570700Sjulian	 * Sometimes a generic message may be statically allocated
295670700Sjulian	 * to avoid problems with allocating when in tight memeory situations.
295770700Sjulian	 * Don't free it if it is so.
295870700Sjulian	 * I break them appart here, because erros may cause a free if the item
295970700Sjulian	 * in which case we'd be doing it twice.
296070700Sjulian	 * they are kept together above, to simplify freeing.
296170700Sjulian	 */
296270700Sjulianout:
296370700Sjulian	NG_RESPOND_MSG(error, here, item, resp);
296471849Sjulian	if (msg)
296570700Sjulian		NG_FREE_MSG(msg);
296652419Sjulian	return (error);
296752419Sjulian}
296852419Sjulian
296952419Sjulian/************************************************************************
2970146213Sglebius			Queue element get/free routines
2971146213Sglebius************************************************************************/
2972146213Sglebius
2973146213Sglebiusuma_zone_t			ng_qzone;
2974146213Sglebiusstatic int			maxalloc = 512;	/* limit the damage of a leak */
2975146213Sglebius
2976146213SglebiusTUNABLE_INT("net.graph.maxalloc", &maxalloc);
2977146213SglebiusSYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc,
2978146213Sglebius    0, "Maximum number of queue items to allocate");
2979146213Sglebius
2980146213Sglebius#ifdef	NETGRAPH_DEBUG
2981146213Sglebiusstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
2982146213Sglebiusstatic int			allocated;	/* number of items malloc'd */
2983146213Sglebius#endif
2984146213Sglebius
2985146213Sglebius/*
2986146213Sglebius * Get a queue entry.
2987146213Sglebius * This is usually called when a packet first enters netgraph.
2988146213Sglebius * By definition, this is usually from an interrupt, or from a user.
2989146213Sglebius * Users are not so important, but try be quick for the times that it's
2990146213Sglebius * an interrupt.
2991146213Sglebius */
2992146213Sglebiusstatic __inline item_p
2993146281Sglebiusng_getqblk(int flags)
2994146213Sglebius{
2995146213Sglebius	item_p item = NULL;
2996146281Sglebius	int wait;
2997146213Sglebius
2998146281Sglebius	wait = (flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT;
2999146213Sglebius
3000146281Sglebius	item = uma_zalloc(ng_qzone, wait | M_ZERO);
3001146281Sglebius
3002146213Sglebius#ifdef	NETGRAPH_DEBUG
3003146213Sglebius	if (item) {
3004146213Sglebius			mtx_lock(&ngq_mtx);
3005146213Sglebius			TAILQ_INSERT_TAIL(&ng_itemlist, item, all);
3006146213Sglebius			allocated++;
3007146213Sglebius			mtx_unlock(&ngq_mtx);
3008146213Sglebius	}
3009146213Sglebius#endif
3010146213Sglebius
3011146213Sglebius	return (item);
3012146213Sglebius}
3013146213Sglebius
3014146213Sglebius/*
3015146213Sglebius * Release a queue entry
3016146213Sglebius */
3017146213Sglebiusvoid
3018146213Sglebiusng_free_item(item_p item)
3019146213Sglebius{
3020151283Sglebius	KASSERT(item->apply == NULL, ("%s: leaking apply callback", __func__));
3021151283Sglebius
3022146213Sglebius	/*
3023146213Sglebius	 * The item may hold resources on it's own. We need to free
3024146213Sglebius	 * these before we can free the item. What they are depends upon
3025146213Sglebius	 * what kind of item it is. it is important that nodes zero
3026146213Sglebius	 * out pointers to resources that they remove from the item
3027146213Sglebius	 * or we release them again here.
3028146213Sglebius	 */
3029146213Sglebius	switch (item->el_flags & NGQF_TYPE) {
3030146213Sglebius	case NGQF_DATA:
3031146213Sglebius		/* If we have an mbuf still attached.. */
3032146213Sglebius		NG_FREE_M(_NGI_M(item));
3033146213Sglebius		break;
3034146213Sglebius	case NGQF_MESG:
3035146213Sglebius		_NGI_RETADDR(item) = 0;
3036146213Sglebius		NG_FREE_MSG(_NGI_MSG(item));
3037146213Sglebius		break;
3038146213Sglebius	case NGQF_FN:
3039146213Sglebius		/* nothing to free really, */
3040146213Sglebius		_NGI_FN(item) = NULL;
3041146213Sglebius		_NGI_ARG1(item) = NULL;
3042146213Sglebius		_NGI_ARG2(item) = 0;
3043146213Sglebius	case NGQF_UNDEF:
3044146213Sglebius		break;
3045146213Sglebius	}
3046146213Sglebius	/* If we still have a node or hook referenced... */
3047146213Sglebius	_NGI_CLR_NODE(item);
3048146213Sglebius	_NGI_CLR_HOOK(item);
3049146213Sglebius
3050146213Sglebius#ifdef	NETGRAPH_DEBUG
3051146213Sglebius	mtx_lock(&ngq_mtx);
3052146213Sglebius	TAILQ_REMOVE(&ng_itemlist, item, all);
3053146213Sglebius	allocated--;
3054146213Sglebius	mtx_unlock(&ngq_mtx);
3055146213Sglebius#endif
3056146213Sglebius	uma_zfree(ng_qzone, item);
3057146213Sglebius}
3058146213Sglebius
3059146213Sglebius/************************************************************************
306052419Sjulian			Module routines
306152419Sjulian************************************************************************/
306252419Sjulian
306352419Sjulian/*
306452419Sjulian * Handle the loading/unloading of a netgraph node type module
306552419Sjulian */
306652419Sjulianint
306752419Sjulianng_mod_event(module_t mod, int event, void *data)
306852419Sjulian{
306952419Sjulian	struct ng_type *const type = data;
307052419Sjulian	int s, error = 0;
307152419Sjulian
307252419Sjulian	switch (event) {
307352419Sjulian	case MOD_LOAD:
307452419Sjulian
307552419Sjulian		/* Register new netgraph node type */
307652419Sjulian		s = splnet();
307752419Sjulian		if ((error = ng_newtype(type)) != 0) {
307852419Sjulian			splx(s);
307952419Sjulian			break;
308052419Sjulian		}
308152419Sjulian
308252419Sjulian		/* Call type specific code */
308352419Sjulian		if (type->mod_event != NULL)
308470700Sjulian			if ((error = (*type->mod_event)(mod, event, data))) {
308572200Sbmilekic				mtx_lock(&ng_typelist_mtx);
308671603Sjulian				type->refs--;	/* undo it */
308752419Sjulian				LIST_REMOVE(type, types);
308872200Sbmilekic				mtx_unlock(&ng_typelist_mtx);
308970700Sjulian			}
309052419Sjulian		splx(s);
309152419Sjulian		break;
309252419Sjulian
309352419Sjulian	case MOD_UNLOAD:
309452419Sjulian		s = splnet();
309571603Sjulian		if (type->refs > 1) {		/* make sure no nodes exist! */
309652419Sjulian			error = EBUSY;
309771603Sjulian		} else {
309871603Sjulian			if (type->refs == 0) {
309971603Sjulian				/* failed load, nothing to undo */
310071603Sjulian				splx(s);
310171603Sjulian				break;
310271603Sjulian			}
310352419Sjulian			if (type->mod_event != NULL) {	/* check with type */
310452419Sjulian				error = (*type->mod_event)(mod, event, data);
310552419Sjulian				if (error != 0) {	/* type refuses.. */
310652419Sjulian					splx(s);
310752419Sjulian					break;
310852419Sjulian				}
310952419Sjulian			}
311072200Sbmilekic			mtx_lock(&ng_typelist_mtx);
311152419Sjulian			LIST_REMOVE(type, types);
311272200Sbmilekic			mtx_unlock(&ng_typelist_mtx);
311352419Sjulian		}
311452419Sjulian		splx(s);
311552419Sjulian		break;
311652419Sjulian
311752419Sjulian	default:
311852419Sjulian		if (type->mod_event != NULL)
311952419Sjulian			error = (*type->mod_event)(mod, event, data);
312052419Sjulian		else
3121132199Sphk			error = EOPNOTSUPP;		/* XXX ? */
312252419Sjulian		break;
312352419Sjulian	}
312452419Sjulian	return (error);
312552419Sjulian}
312652419Sjulian
312752419Sjulian/*
312852419Sjulian * Handle loading and unloading for this code.
312952419Sjulian * The only thing we need to link into is the NETISR strucure.
313052419Sjulian */
313152419Sjulianstatic int
313252419Sjulianngb_mod_event(module_t mod, int event, void *data)
313352419Sjulian{
3134146212Sglebius	int error = 0;
313552419Sjulian
313652419Sjulian	switch (event) {
313752419Sjulian	case MOD_LOAD:
3138146212Sglebius		/* Initialize everything. */
313993818Sjhb		mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_SPIN);
3140123278Struckman		mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL,
3141123278Struckman		    MTX_DEF);
3142123278Struckman		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL,
3143123278Struckman		    MTX_DEF);
3144123278Struckman		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL,
3145123278Struckman		    MTX_DEF);
3146151974Sglebius		mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL,
3147151974Sglebius		    MTX_DEF);
3148146212Sglebius#ifdef	NETGRAPH_DEBUG
3149146212Sglebius		mtx_init(&ngq_mtx, "netgraph item list mutex", NULL,
3150123278Struckman		    MTX_DEF);
3151146212Sglebius#endif
3152146212Sglebius		ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item),
3153146212Sglebius		    NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
3154146212Sglebius		uma_zone_set_max(ng_qzone, maxalloc);
3155141719Sglebius		netisr_register(NETISR_NETGRAPH, (netisr_t *)ngintr, NULL,
3156141719Sglebius		    NETISR_MPSAFE);
315752419Sjulian		break;
315852419Sjulian	case MOD_UNLOAD:
315952419Sjulian		/* You cant unload it because an interface may be using it.  */
316052419Sjulian		error = EBUSY;
316152419Sjulian		break;
316252419Sjulian	default:
316352419Sjulian		error = EOPNOTSUPP;
316452419Sjulian		break;
316552419Sjulian	}
316652419Sjulian	return (error);
316752419Sjulian}
316852419Sjulian
316952419Sjulianstatic moduledata_t netgraph_mod = {
317052419Sjulian	"netgraph",
317152419Sjulian	ngb_mod_event,
317252419Sjulian	(NULL)
317352419Sjulian};
3174139774SemaxDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE);
317572946SjulianSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family");
317672946SjulianSYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,"");
317772946SjulianSYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, "");
317852419Sjulian
317970784Sjulian#ifdef	NETGRAPH_DEBUG
318070700Sjulianvoid
318170784Sjuliandumphook (hook_p hook, char *file, int line)
318270784Sjulian{
318370784Sjulian	printf("hook: name %s, %d refs, Last touched:\n",
318470784Sjulian		_NG_HOOK_NAME(hook), hook->hk_refs);
318570784Sjulian	printf("	Last active @ %s, line %d\n",
318670784Sjulian		hook->lastfile, hook->lastline);
318770784Sjulian	if (line) {
318870784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
318970784Sjulian	}
319070784Sjulian}
319170784Sjulian
319270784Sjulianvoid
319370784Sjuliandumpnode(node_p node, char *file, int line)
319470784Sjulian{
319570784Sjulian	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
319671047Sjulian		_NG_NODE_ID(node), node->nd_type->name,
319770784Sjulian		node->nd_numhooks, node->nd_flags,
319870784Sjulian		node->nd_refs, node->nd_name);
319970784Sjulian	printf("	Last active @ %s, line %d\n",
320070784Sjulian		node->lastfile, node->lastline);
320170784Sjulian	if (line) {
320270784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
320370784Sjulian	}
320470784Sjulian}
320570784Sjulian
320670784Sjulianvoid
320770700Sjuliandumpitem(item_p item, char *file, int line)
320870700Sjulian{
3209146212Sglebius	printf(" ACTIVE item, last used at %s, line %d",
3210146212Sglebius		item->lastfile, item->lastline);
3211146212Sglebius	switch(item->el_flags & NGQF_TYPE) {
3212146212Sglebius	case NGQF_DATA:
3213146212Sglebius		printf(" - [data]\n");
3214146212Sglebius		break;
3215146212Sglebius	case NGQF_MESG:
3216146212Sglebius		printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
3217146212Sglebius		break;
3218146212Sglebius	case NGQF_FN:
3219146212Sglebius		printf(" - fn@%p (%p, %p, %p, %d (%x))\n",
3220146212Sglebius			item->body.fn.fn_fn,
3221149735Sglebius			_NGI_NODE(item),
3222149735Sglebius			_NGI_HOOK(item),
3223146212Sglebius			item->body.fn.fn_arg1,
3224146212Sglebius			item->body.fn.fn_arg2,
3225146212Sglebius			item->body.fn.fn_arg2);
3226146212Sglebius		break;
3227146212Sglebius	case NGQF_UNDEF:
3228146212Sglebius		printf(" - UNDEFINED!\n");
322952419Sjulian	}
323070784Sjulian	if (line) {
323170784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
3232149735Sglebius		if (_NGI_NODE(item)) {
323370784Sjulian			printf("node %p ([%x])\n",
3234149735Sglebius				_NGI_NODE(item), ng_node2ID(_NGI_NODE(item)));
323570784Sjulian		}
323670784Sjulian	}
323770700Sjulian}
323852419Sjulian
323970784Sjulianstatic void
324070784Sjulianng_dumpitems(void)
324170784Sjulian{
324270784Sjulian	item_p item;
324370784Sjulian	int i = 1;
324470784Sjulian	TAILQ_FOREACH(item, &ng_itemlist, all) {
324570784Sjulian		printf("[%d] ", i++);
324670784Sjulian		dumpitem(item, NULL, 0);
324770784Sjulian	}
324870784Sjulian}
324970784Sjulian
325070784Sjulianstatic void
325170784Sjulianng_dumpnodes(void)
325270784Sjulian{
325370784Sjulian	node_p node;
325470784Sjulian	int i = 1;
3255131008Srwatson	mtx_lock(&ng_nodelist_mtx);
325670784Sjulian	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
325770784Sjulian		printf("[%d] ", i++);
325870784Sjulian		dumpnode(node, NULL, 0);
325970784Sjulian	}
3260131008Srwatson	mtx_unlock(&ng_nodelist_mtx);
326170784Sjulian}
326270784Sjulian
326370784Sjulianstatic void
326470784Sjulianng_dumphooks(void)
326570784Sjulian{
326670784Sjulian	hook_p hook;
326770784Sjulian	int i = 1;
3268131008Srwatson	mtx_lock(&ng_nodelist_mtx);
326970784Sjulian	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
327070784Sjulian		printf("[%d] ", i++);
327170784Sjulian		dumphook(hook, NULL, 0);
327270784Sjulian	}
3273131008Srwatson	mtx_unlock(&ng_nodelist_mtx);
327470784Sjulian}
327570784Sjulian
327670700Sjulianstatic int
327770700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
327870700Sjulian{
327970700Sjulian	int error;
328070700Sjulian	int val;
328170700Sjulian	int i;
328270700Sjulian
328370700Sjulian	val = allocated;
328470700Sjulian	i = 1;
328570700Sjulian	error = sysctl_handle_int(oidp, &val, sizeof(int), req);
328671047Sjulian	if (error != 0 || req->newptr == NULL)
328771047Sjulian		return (error);
328871047Sjulian	if (val == 42) {
328970784Sjulian		ng_dumpitems();
329070784Sjulian		ng_dumpnodes();
329170784Sjulian		ng_dumphooks();
329270700Sjulian	}
329371047Sjulian	return (0);
329452419Sjulian}
329552419Sjulian
329671047SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW,
329771047Sjulian    0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items");
329870784Sjulian#endif	/* NETGRAPH_DEBUG */
329970700Sjulian
330070700Sjulian
330170700Sjulian/***********************************************************************
330270700Sjulian* Worklist routines
330370700Sjulian**********************************************************************/
330470700Sjulian/* NETISR thread enters here */
330552419Sjulian/*
330670700Sjulian * Pick a node off the list of nodes with work,
330770700Sjulian * try get an item to process off it.
330870700Sjulian * If there are no more, remove the node from the list.
330952419Sjulian */
331070700Sjulianstatic void
331170700Sjulianngintr(void)
331252419Sjulian{
331370700Sjulian	item_p item;
331470700Sjulian	node_p  node = NULL;
331552419Sjulian
331670700Sjulian	for (;;) {
331772200Sbmilekic		mtx_lock_spin(&ng_worklist_mtx);
331870700Sjulian		node = TAILQ_FIRST(&ng_worklist);
331970700Sjulian		if (!node) {
332072200Sbmilekic			mtx_unlock_spin(&ng_worklist_mtx);
332170700Sjulian			break;
332269922Sjulian		}
3323132464Sjulian		node->nd_flags &= ~NGF_WORKQ;
332470784Sjulian		TAILQ_REMOVE(&ng_worklist, node, nd_work);
332572200Sbmilekic		mtx_unlock_spin(&ng_worklist_mtx);
3326154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist",
3327154275Sglebius		    __func__, node->nd_ID, node);
332870700Sjulian		/*
332970700Sjulian		 * We have the node. We also take over the reference
333070700Sjulian		 * that the list had on it.
333170700Sjulian		 * Now process as much as you can, until it won't
333270700Sjulian		 * let you have another item off the queue.
333370700Sjulian		 * All this time, keep the reference
333470700Sjulian		 * that lets us be sure that the node still exists.
333570700Sjulian		 * Let the reference go at the last minute.
333671902Sjulian		 * ng_dequeue will put us back on the worklist
333771902Sjulian		 * if there is more too do. This may be of use if there
3338152451Sglebius		 * are Multiple Processors and multiple Net threads in the
333971902Sjulian		 * future.
334070700Sjulian		 */
334170700Sjulian		for (;;) {
3342151238Sglebius			int rw;
3343151238Sglebius
334472200Sbmilekic			mtx_lock_spin(&node->nd_input_queue.q_mtx);
3345151238Sglebius			item = ng_dequeue(&node->nd_input_queue, &rw);
334670700Sjulian			if (item == NULL) {
334772200Sbmilekic				mtx_unlock_spin(&node->nd_input_queue.q_mtx);
334870700Sjulian				break; /* go look for another node */
334970700Sjulian			} else {
335072200Sbmilekic				mtx_unlock_spin(&node->nd_input_queue.q_mtx);
335174078Sjulian				NGI_GET_NODE(item, node); /* zaps stored node */
3352151238Sglebius				ng_apply_item(node, item, rw);
335374078Sjulian				NG_NODE_UNREF(node);
335470700Sjulian			}
335570700Sjulian		}
335673238Sjulian		NG_NODE_UNREF(node);
335752419Sjulian	}
335870700Sjulian}
335969922Sjulian
336070700Sjulianstatic void
336170700Sjulianng_worklist_remove(node_p node)
336270700Sjulian{
3363154225Sglebius	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3364154225Sglebius
336572200Sbmilekic	mtx_lock_spin(&ng_worklist_mtx);
3366132464Sjulian	if (node->nd_flags & NGF_WORKQ) {
3367132464Sjulian		node->nd_flags &= ~NGF_WORKQ;
336870784Sjulian		TAILQ_REMOVE(&ng_worklist, node, nd_work);
336972979Sjulian		mtx_unlock_spin(&ng_worklist_mtx);
337070784Sjulian		NG_NODE_UNREF(node);
3371154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) removed from worklist",
3372154275Sglebius		    __func__, node->nd_ID, node);
337372979Sjulian	} else {
337472979Sjulian		mtx_unlock_spin(&ng_worklist_mtx);
337570700Sjulian	}
337670700Sjulian}
337770700Sjulian
337872979Sjulian/*
337972979Sjulian * XXX
338072979Sjulian * It's posible that a debugging NG_NODE_REF may need
338172979Sjulian * to be outside the mutex zone
338272979Sjulian */
338370700Sjulianstatic void
338470700Sjulianng_setisr(node_p node)
338570700Sjulian{
3386148236Sglebius
3387148266Sglebius	mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED);
3388148236Sglebius
3389132464Sjulian	if ((node->nd_flags & NGF_WORKQ) == 0) {
339069922Sjulian		/*
339170700Sjulian		 * If we are not already on the work queue,
339270700Sjulian		 * then put us on.
339369922Sjulian		 */
3394132464Sjulian		node->nd_flags |= NGF_WORKQ;
3395148236Sglebius		mtx_lock_spin(&ng_worklist_mtx);
339670784Sjulian		TAILQ_INSERT_TAIL(&ng_worklist, node, nd_work);
3397148236Sglebius		mtx_unlock_spin(&ng_worklist_mtx);
339872979Sjulian		NG_NODE_REF(node); /* XXX fafe in mutex? */
3399154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__,
3400154275Sglebius		    node->nd_ID, node);
3401154225Sglebius	} else
3402154275Sglebius		CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist",
3403154275Sglebius		    __func__, node->nd_ID, node);
340470700Sjulian	schednetisr(NETISR_NETGRAPH);
340570700Sjulian}
340670700Sjulian
340770700Sjulian
340870700Sjulian/***********************************************************************
340970700Sjulian* Externally useable functions to set up a queue item ready for sending
341070700Sjulian***********************************************************************/
341170700Sjulian
341270784Sjulian#ifdef	NETGRAPH_DEBUG
341370784Sjulian#define	ITEM_DEBUG_CHECKS						\
341470700Sjulian	do {								\
341571849Sjulian		if (NGI_NODE(item) ) {					\
341670700Sjulian			printf("item already has node");		\
3417131933Smarcel			kdb_enter("has node");				\
341871849Sjulian			NGI_CLR_NODE(item);				\
341970700Sjulian		}							\
342071849Sjulian		if (NGI_HOOK(item) ) {					\
342170700Sjulian			printf("item already has hook");		\
3422131933Smarcel			kdb_enter("has hook");				\
342371849Sjulian			NGI_CLR_HOOK(item);				\
342470700Sjulian		}							\
342570700Sjulian	} while (0)
342670700Sjulian#else
342770784Sjulian#define ITEM_DEBUG_CHECKS
342870700Sjulian#endif
342970700Sjulian
343070700Sjulian/*
3431131374Sjulian * Put mbuf into the item.
343270700Sjulian * Hook and node references will be removed when the item is dequeued.
343370700Sjulian * (or equivalent)
343470700Sjulian * (XXX) Unsafe because no reference held by peer on remote node.
343570700Sjulian * remote node might go away in this timescale.
343670700Sjulian * We know the hooks can't go away because that would require getting
343770700Sjulian * a writer item on both nodes and we must have at least a  reader
3438151973Sglebius * here to be able to do this.
343970700Sjulian * Note that the hook loaded is the REMOTE hook.
344070700Sjulian *
344170700Sjulian * This is possibly in the critical path for new data.
344270700Sjulian */
344370700Sjulianitem_p
3444146281Sglebiusng_package_data(struct mbuf *m, int flags)
344570700Sjulian{
344670700Sjulian	item_p item;
344770700Sjulian
3448146281Sglebius	if ((item = ng_getqblk(flags)) == NULL) {
344970700Sjulian		NG_FREE_M(m);
345070700Sjulian		return (NULL);
345170700Sjulian	}
345270784Sjulian	ITEM_DEBUG_CHECKS;
3453149505Sglebius	item->el_flags = NGQF_DATA | NGQF_READER;
345470700Sjulian	item->el_next = NULL;
345570700Sjulian	NGI_M(item) = m;
345670700Sjulian	return (item);
345770700Sjulian}
345870700Sjulian
345970700Sjulian/*
346070700Sjulian * Allocate a queue item and put items into it..
346170700Sjulian * Evaluate the address as this will be needed to queue it and
346270700Sjulian * to work out what some of the fields should be.
346370700Sjulian * Hook and node references will be removed when the item is dequeued.
346470700Sjulian * (or equivalent)
346570700Sjulian */
346670700Sjulianitem_p
3467146281Sglebiusng_package_msg(struct ng_mesg *msg, int flags)
346870700Sjulian{
346970700Sjulian	item_p item;
347070700Sjulian
3471146281Sglebius	if ((item = ng_getqblk(flags)) == NULL) {
347271849Sjulian		NG_FREE_MSG(msg);
347370700Sjulian		return (NULL);
347469922Sjulian	}
347570784Sjulian	ITEM_DEBUG_CHECKS;
3476149505Sglebius	/* Messages items count as writers unless explicitly exempted. */
3477149505Sglebius	if (msg->header.cmd & NGM_READONLY)
3478149505Sglebius		item->el_flags = NGQF_MESG | NGQF_READER;
3479149505Sglebius	else
3480149505Sglebius		item->el_flags = NGQF_MESG | NGQF_WRITER;
348170700Sjulian	item->el_next = NULL;
348270700Sjulian	/*
348370700Sjulian	 * Set the current lasthook into the queue item
348470700Sjulian	 */
348570700Sjulian	NGI_MSG(item) = msg;
3486102244Sarchie	NGI_RETADDR(item) = 0;
348770700Sjulian	return (item);
348870700Sjulian}
348969922Sjulian
349070700Sjulian
349170700Sjulian
349271849Sjulian#define SET_RETADDR(item, here, retaddr)				\
349371047Sjulian	do {	/* Data or fn items don't have retaddrs */		\
349471047Sjulian		if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) {	\
349570700Sjulian			if (retaddr) {					\
349670700Sjulian				NGI_RETADDR(item) = retaddr;		\
349770700Sjulian			} else {					\
349870700Sjulian				/*					\
349970700Sjulian				 * The old return address should be ok.	\
350070700Sjulian				 * If there isn't one, use the address	\
350170700Sjulian				 * here.				\
350270700Sjulian				 */					\
350370700Sjulian				if (NGI_RETADDR(item) == 0) {		\
350470700Sjulian					NGI_RETADDR(item)		\
350570700Sjulian						= ng_node2ID(here);	\
350670700Sjulian				}					\
350770700Sjulian			}						\
350870700Sjulian		}							\
350970700Sjulian	} while (0)
351070700Sjulian
351170700Sjulianint
351270700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
351370700Sjulian{
351471849Sjulian	hook_p peer;
351571849Sjulian	node_p peernode;
351670784Sjulian	ITEM_DEBUG_CHECKS;
351770700Sjulian	/*
351870700Sjulian	 * Quick sanity check..
351970784Sjulian	 * Since a hook holds a reference on it's node, once we know
352070784Sjulian	 * that the peer is still connected (even if invalid,) we know
352170784Sjulian	 * that the peer node is present, though maybe invalid.
352270700Sjulian	 */
352370700Sjulian	if ((hook == NULL)
352470784Sjulian	|| NG_HOOK_NOT_VALID(hook)
352570784Sjulian	|| (NG_HOOK_PEER(hook) == NULL)
352670784Sjulian	|| NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))
352770784Sjulian	|| NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) {
352870700Sjulian		NG_FREE_ITEM(item);
352971047Sjulian		TRAP_ERROR();
353073083Sjulian		return (ENETDOWN);
353152419Sjulian	}
353252419Sjulian
353370700Sjulian	/*
353470700Sjulian	 * Transfer our interest to the other (peer) end.
353570700Sjulian	 */
353671849Sjulian	peer = NG_HOOK_PEER(hook);
353771849Sjulian	NG_HOOK_REF(peer);
353871849Sjulian	NGI_SET_HOOK(item, peer);
353971849Sjulian	peernode = NG_PEER_NODE(hook);
354071849Sjulian	NG_NODE_REF(peernode);
354171849Sjulian	NGI_SET_NODE(item, peernode);
354279706Sjulian	SET_RETADDR(item, here, retaddr);
354370700Sjulian	return (0);
354470700Sjulian}
354552419Sjulian
354670700Sjulianint
3547152451Sglebiusng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
354870700Sjulian{
3549152451Sglebius	node_p	dest = NULL;
355070700Sjulian	hook_p	hook = NULL;
3551152451Sglebius	int	error;
355270700Sjulian
355370784Sjulian	ITEM_DEBUG_CHECKS;
355470700Sjulian	/*
355570700Sjulian	 * Note that ng_path2noderef increments the reference count
355670700Sjulian	 * on the node for us if it finds one. So we don't have to.
355770700Sjulian	 */
355870700Sjulian	error = ng_path2noderef(here, address, &dest, &hook);
355970700Sjulian	if (error) {
356070700Sjulian		NG_FREE_ITEM(item);
356170784Sjulian		return (error);
356252419Sjulian	}
356371849Sjulian	NGI_SET_NODE(item, dest);
356471849Sjulian	if ( hook) {
356570784Sjulian		NG_HOOK_REF(hook);	/* don't let it go while on the queue */
356671849Sjulian		NGI_SET_HOOK(item, hook);
356771849Sjulian	}
356871849Sjulian	SET_RETADDR(item, here, retaddr);
356970700Sjulian	return (0);
357070700Sjulian}
357152419Sjulian
357270700Sjulianint
357370700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
357470700Sjulian{
357570700Sjulian	node_p dest;
357670700Sjulian
357770784Sjulian	ITEM_DEBUG_CHECKS;
357870700Sjulian	/*
357970700Sjulian	 * Find the target node.
358070700Sjulian	 */
358170700Sjulian	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
358270700Sjulian	if (dest == NULL) {
358370700Sjulian		NG_FREE_ITEM(item);
358471047Sjulian		TRAP_ERROR();
358570700Sjulian		return(EINVAL);
358670700Sjulian	}
358770700Sjulian	/* Fill out the contents */
358871849Sjulian	NGI_SET_NODE(item, dest);
358971849Sjulian	NGI_CLR_HOOK(item);
359071849Sjulian	SET_RETADDR(item, here, retaddr);
359152419Sjulian	return (0);
359252419Sjulian}
359352419Sjulian
359452419Sjulian/*
359570700Sjulian * special case to send a message to self (e.g. destroy node)
359670700Sjulian * Possibly indicate an arrival hook too.
359770700Sjulian * Useful for removing that hook :-)
359852419Sjulian */
359970700Sjulianitem_p
360070700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
360152419Sjulian{
360270700Sjulian	item_p item;
360352419Sjulian
360470700Sjulian	/*
360570700Sjulian	 * Find the target node.
360670700Sjulian	 * If there is a HOOK argument, then use that in preference
360770700Sjulian	 * to the address.
360870700Sjulian	 */
3609146281Sglebius	if ((item = ng_getqblk(NG_NOFLAGS)) == NULL) {
361071849Sjulian		NG_FREE_MSG(msg);
361170700Sjulian		return (NULL);
361252419Sjulian	}
361370700Sjulian
361470700Sjulian	/* Fill out the contents */
3615149505Sglebius	item->el_flags = NGQF_MESG | NGQF_WRITER;
361670700Sjulian	item->el_next = NULL;
361770784Sjulian	NG_NODE_REF(here);
361871849Sjulian	NGI_SET_NODE(item, here);
361971849Sjulian	if (hook) {
362070784Sjulian		NG_HOOK_REF(hook);
362171849Sjulian		NGI_SET_HOOK(item, hook);
362271849Sjulian	}
362370700Sjulian	NGI_MSG(item) = msg;
362470700Sjulian	NGI_RETADDR(item) = ng_node2ID(here);
362570700Sjulian	return (item);
362652419Sjulian}
362752419Sjulian
3628146281Sglebiusint
3629146180Sglebiusng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2,
3630146281Sglebius	int flags)
363171047Sjulian{
363271047Sjulian	item_p item;
363371047Sjulian
3634146281Sglebius	if ((item = ng_getqblk(flags)) == NULL) {
363571047Sjulian		return (ENOMEM);
363671047Sjulian	}
363771047Sjulian	item->el_flags = NGQF_FN | NGQF_WRITER;
363873238Sjulian	NG_NODE_REF(node); /* and one for the item */
363971849Sjulian	NGI_SET_NODE(item, node);
364071849Sjulian	if (hook) {
364171047Sjulian		NG_HOOK_REF(hook);
364271849Sjulian		NGI_SET_HOOK(item, hook);
364371047Sjulian	}
364471047Sjulian	NGI_FN(item) = fn;
364571047Sjulian	NGI_ARG1(item) = arg1;
364671047Sjulian	NGI_ARG2(item) = arg2;
3647146281Sglebius	return(ng_snd_item(item, flags));
364871047Sjulian}
364971047Sjulian
3650152451Sglebius/*
365191711Sjulian * Official timeout routines for Netgraph nodes.
365291711Sjulian */
365391711Sjulianstatic void
3654140852Sglebiusng_callout_trampoline(void *arg)
365591711Sjulian{
365691711Sjulian	item_p item = arg;
365791711Sjulian
365891711Sjulian	ng_snd_item(item, 0);
365991711Sjulian}
366091711Sjulian
366191711Sjulian
3662137138Sglebiusint
3663138268Sglebiusng_callout(struct callout *c, node_p node, hook_p hook, int ticks,
366491711Sjulian    ng_item_fn *fn, void * arg1, int arg2)
366591711Sjulian{
3666149881Sglebius	item_p item, oitem;
366791711Sjulian
3668146281Sglebius	if ((item = ng_getqblk(NG_NOFLAGS)) == NULL)
3669137138Sglebius		return (ENOMEM);
3670137138Sglebius
367191711Sjulian	item->el_flags = NGQF_FN | NGQF_WRITER;
367291711Sjulian	NG_NODE_REF(node);		/* and one for the item */
367391711Sjulian	NGI_SET_NODE(item, node);
367491711Sjulian	if (hook) {
367591711Sjulian		NG_HOOK_REF(hook);
367691711Sjulian		NGI_SET_HOOK(item, hook);
367791711Sjulian	}
367891711Sjulian	NGI_FN(item) = fn;
367991711Sjulian	NGI_ARG1(item) = arg1;
368091711Sjulian	NGI_ARG2(item) = arg2;
3681149881Sglebius	oitem = c->c_arg;
3682149881Sglebius	if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 &&
3683149881Sglebius	    oitem != NULL)
3684149881Sglebius		NG_FREE_ITEM(oitem);
3685137138Sglebius	return (0);
368691711Sjulian}
368791711Sjulian
368891711Sjulian/* A special modified version of untimeout() */
3689152451Sglebiusint
3690138268Sglebiusng_uncallout(struct callout *c, node_p node)
369191711Sjulian{
369291711Sjulian	item_p item;
3693137138Sglebius	int rval;
3694149357Sglebius
3695149357Sglebius	KASSERT(c != NULL, ("ng_uncallout: NULL callout"));
3696149357Sglebius	KASSERT(node != NULL, ("ng_uncallout: NULL node"));
3697149357Sglebius
3698137230Sglebius	rval = callout_stop(c);
3699137138Sglebius	item = c->c_arg;
3700137138Sglebius	/* Do an extra check */
3701140852Sglebius	if ((rval > 0) && (c->c_func == &ng_callout_trampoline) &&
3702137138Sglebius	    (NGI_NODE(item) == node)) {
370391711Sjulian		/*
370491711Sjulian		 * We successfully removed it from the queue before it ran
3705152451Sglebius		 * So now we need to unreference everything that was
370691711Sjulian		 * given extra references. (NG_FREE_ITEM does this).
370791711Sjulian		 */
370891711Sjulian		NG_FREE_ITEM(item);
370991711Sjulian	}
3710149881Sglebius	c->c_arg = NULL;
3711137138Sglebius
3712137138Sglebius	return (rval);
371391711Sjulian}
371491711Sjulian
371570700Sjulian/*
371670700Sjulian * Set the address, if none given, give the node here.
371770700Sjulian */
371870700Sjulianvoid
371970700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
372070700Sjulian{
372170700Sjulian	if (retaddr) {
372270700Sjulian		NGI_RETADDR(item) = retaddr;
372370700Sjulian	} else {
372470700Sjulian		/*
372570700Sjulian		 * The old return address should be ok.
372670700Sjulian		 * If there isn't one, use the address here.
372770700Sjulian		 */
372870700Sjulian		NGI_RETADDR(item) = ng_node2ID(here);
372970700Sjulian	}
373070700Sjulian}
373152419Sjulian
373270700Sjulian#define TESTING
373370700Sjulian#ifdef TESTING
373470700Sjulian/* just test all the macros */
373570700Sjulianvoid
373670700Sjulianng_macro_test(item_p item);
373770700Sjulianvoid
373870700Sjulianng_macro_test(item_p item)
373970700Sjulian{
374070700Sjulian	node_p node = NULL;
374170700Sjulian	hook_p hook = NULL;
374270700Sjulian	struct mbuf *m;
374370700Sjulian	struct ng_mesg *msg;
374470700Sjulian	ng_ID_t retaddr;
374570700Sjulian	int	error;
374670700Sjulian
374770700Sjulian	NGI_GET_M(item, m);
374870700Sjulian	NGI_GET_MSG(item, msg);
374970700Sjulian	retaddr = NGI_RETADDR(item);
3750131374Sjulian	NG_SEND_DATA(error, hook, m, NULL);
375170700Sjulian	NG_SEND_DATA_ONLY(error, hook, m);
375270700Sjulian	NG_FWD_NEW_DATA(error, item, hook, m);
375370784Sjulian	NG_FWD_ITEM_HOOK(error, item, hook);
375470700Sjulian	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
375570700Sjulian	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
375670700Sjulian	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
375770700Sjulian	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
375870700Sjulian}
375970700Sjulian#endif /* TESTING */
376070700Sjulian
3761