ng_base.c revision 70912
152419Sjulian/*
252419Sjulian * ng_base.c
352419Sjulian *
452419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
552419Sjulian * All rights reserved.
670700Sjulian *
752419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
852419Sjulian * redistribution of this software, in source or object code forms, with or
952419Sjulian * without modifications are expressly permitted by Whistle Communications;
1052419Sjulian * provided, however, that:
1152419Sjulian * 1. Any and all reproductions of the source or object code must include the
1252419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1352419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1452419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1552419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1652419Sjulian *    such appears in the above copyright notice or in the software.
1770700Sjulian *
1852419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
1952419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2052419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2152419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2252419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2352419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2452419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2552419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2652419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2752419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
2852419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
2952419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3052419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3152419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3252419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3352419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3452419Sjulian * OF SUCH DAMAGE.
3552419Sjulian *
3667506Sjulian * Authors: Julian Elischer <julian@freebsd.org>
3767506Sjulian *          Archie Cobbs <archie@freebsd.org>
3852419Sjulian *
3952419Sjulian * $FreeBSD: head/sys/netgraph/ng_base.c 70912 2001-01-10 23:19:32Z julian $
4052419Sjulian * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
4152419Sjulian */
4252419Sjulian
4352419Sjulian/*
4452419Sjulian * This file implements the base netgraph code.
4552419Sjulian */
4652419Sjulian
4752419Sjulian#include <sys/param.h>
4852419Sjulian#include <sys/systm.h>
4952419Sjulian#include <sys/errno.h>
5052419Sjulian#include <sys/kernel.h>
5152419Sjulian#include <sys/malloc.h>
5252419Sjulian#include <sys/syslog.h>
5370700Sjulian#include <sys/sysctl.h>
5452419Sjulian#include <sys/linker.h>
5552419Sjulian#include <sys/queue.h>
5652419Sjulian#include <sys/mbuf.h>
5752843Sphk#include <sys/ctype.h>
5852816Sarchie#include <machine/limits.h>
5952419Sjulian
6052419Sjulian#include <net/netisr.h>
6152419Sjulian
6252419Sjulian#include <netgraph/ng_message.h>
6352419Sjulian#include <netgraph/netgraph.h>
6453913Sarchie#include <netgraph/ng_parse.h>
6552419Sjulian
6659756SpeterMODULE_VERSION(netgraph, 1);
6759756Speter
6870784Sjulian/* List of all active nodes */
6970700Sjulianstatic LIST_HEAD(, ng_node) ng_nodelist;
7070700Sjulianstatic struct mtx	ng_nodelist_mtx;
7152419Sjulian
7270784Sjulian#ifdef	NETGRAPH_DEBUG
7370784Sjulian
7470784Sjulianstatic SLIST_HEAD(, ng_node) ng_allnodes;
7570784Sjulianstatic LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */
7670784Sjulianstatic SLIST_HEAD(, ng_hook) ng_allhooks;
7770784Sjulianstatic LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */
7870784Sjulian
7970784Sjulianstatic void ng_dumpitems(void);
8070784Sjulianstatic void ng_dumpnodes(void);
8170784Sjulianstatic void ng_dumphooks(void);
8270784Sjulian
8370784Sjulian#endif	/* NETGRAPH_DEBUG */
8470784Sjulian
8570700Sjulian/* List nodes with unallocated work */
8670700Sjulianstatic TAILQ_HEAD(, ng_node) ng_worklist = TAILQ_HEAD_INITIALIZER(ng_worklist);
8770700Sjulianstatic struct mtx	ng_worklist_mtx;
8870700Sjulian
8952419Sjulian/* List of installed types */
9070700Sjulianstatic LIST_HEAD(, ng_type) ng_typelist;
9170700Sjulianstatic struct mtx	ng_typelist_mtx;
9252419Sjulian
9370700Sjulian/* Hash related definitions */
9470700Sjulian/* Don't nead to initialise them because it's a LIST */
9552722Sjulian#define ID_HASH_SIZE 32 /* most systems wont need even this many */
9670700Sjulianstatic LIST_HEAD(, ng_node) ng_ID_hash[ID_HASH_SIZE];
9770700Sjulianstatic struct mtx	ng_idhash_mtx;
9852722Sjulian
9970700Sjulian/* Mutex that protects the free queue item list */
10070700Sjulianstatic volatile item_p		ngqfree;	/* free ones */
10170700Sjulianstatic struct mtx	ngq_mtx;
10270700Sjulian
10352419Sjulian/* Internal functions */
10452419Sjulianstatic int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
10552419Sjulianstatic int	ng_connect(hook_p hook1, hook_p hook2);
10652419Sjulianstatic void	ng_disconnect_hook(hook_p hook);
10770700Sjulianstatic int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
10852722Sjulianstatic ng_ID_t	ng_decodeidname(const char *name);
10952419Sjulianstatic int	ngb_mod_event(module_t mod, int event, void *data);
11070700Sjulianstatic void	ng_worklist_remove(node_p node);
11152419Sjulianstatic void	ngintr(void);
11270700Sjulianstatic int	ng_apply_item(node_p node, item_p item);
11370700Sjulianstatic void	ng_flush_input_queue(struct ng_queue * ngq);
11470700Sjulianstatic void	ng_setisr(node_p node);
11570700Sjulianstatic node_p	ng_ID2noderef(ng_ID_t ID);
11652419Sjulian
11770784Sjulian/* imported , these used to be externally visible, some may go back */
11870700Sjulianint	ng_bypass(hook_p hook1, hook_p hook2);
11970700Sjulianvoid	ng_cutlinks(node_p node);
12070700Sjulianint	ng_con_nodes(node_p node, const char *name, node_p node2,
12170700Sjulian	const char *name2);
12270700Sjulianvoid	ng_destroy_hook(hook_p hook);
12370700Sjuliannode_p	ng_name2noderef(node_p node, const char *name);
12470700Sjulianint	ng_path2noderef(node_p here, const char *path,
12570700Sjulian	node_p *dest, hook_p *lasthook);
12670700Sjulianstruct	ng_type *ng_findtype(const char *type);
12770700Sjulianint	ng_make_node(const char *type, node_p *nodepp);
12870700Sjulianint	ng_mkpeer(node_p node, const char *name, const char *name2, char *type);
12970700Sjulianint	ng_path_parse(char *addr, char **node, char **path, char **hook);
13070700Sjulianvoid	ng_rmnode(node_p node);
13170784Sjulianvoid	ng_unname(node_p node);
13270700Sjulian
13370700Sjulian
13452419Sjulian/* Our own netgraph malloc type */
13552419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
13670700SjulianMALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures");
13770700SjulianMALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures");
13870700SjulianMALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures");
13970700SjulianMALLOC_DEFINE(M_NETGRAPH_META, "netgraph_meta", "netgraph name storage");
14070700SjulianMALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
14152419Sjulian
14270700Sjulian/* Should not be visible outside this file */
14370784Sjulian
14470784Sjulian#define _NG_ALLOC_HOOK(hook) \
14570784Sjulian	MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO)
14670784Sjulian#define _NG_ALLOC_NODE(node) \
14770784Sjulian	MALLOC(node, node_p, sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO)
14870784Sjulian
14970784Sjulian#ifdef NETGRAPH_DEBUG /*----------------------------------------------*/
15070784Sjulian/*
15170784Sjulian * In debug mode:
15270784Sjulian * In an attempt to help track reference count screwups
15370784Sjulian * we do not free objects back to the malloc system, but keep them
15470784Sjulian * in a local cache where we can examine them and keep information safely
15570784Sjulian * after they have been freed.
15670784Sjulian * We use this scheme for nodes and hooks, and to some extent for items.
15770784Sjulian */
15870784Sjulianstatic __inline hook_p
15970784Sjulianng_alloc_hook(void)
16070784Sjulian{
16170784Sjulian	hook_p hook;
16270784Sjulian	SLIST_ENTRY(ng_hook) temp;
16370784Sjulian	mtx_enter(&ng_nodelist_mtx, MTX_DEF);
16470784Sjulian	hook = LIST_FIRST(&ng_freehooks);
16570784Sjulian	if (hook) {
16670784Sjulian		LIST_REMOVE(hook, hk_hooks);
16770784Sjulian		bcopy(&hook->hk_all, &temp, sizeof(temp));
16870784Sjulian		bzero(hook, sizeof(struct ng_hook));
16970784Sjulian		bcopy(&temp, &hook->hk_all, sizeof(temp));
17070784Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
17170784Sjulian		hook->hk_magic = HK_MAGIC;
17270784Sjulian	} else {
17370784Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
17470784Sjulian		_NG_ALLOC_HOOK(hook);
17570784Sjulian		if (hook) {
17670784Sjulian			hook->hk_magic = HK_MAGIC;
17770784Sjulian			mtx_enter(&ng_nodelist_mtx, MTX_DEF);
17870784Sjulian			SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all);
17970784Sjulian			mtx_exit(&ng_nodelist_mtx, MTX_DEF);
18070784Sjulian		}
18170784Sjulian	}
18270784Sjulian	return (hook);
18370784Sjulian}
18470784Sjulian
18570784Sjulianstatic __inline node_p
18670784Sjulianng_alloc_node(void)
18770784Sjulian{
18870784Sjulian	node_p node;
18970784Sjulian	SLIST_ENTRY(ng_node) temp;
19070784Sjulian	mtx_enter(&ng_nodelist_mtx, MTX_DEF);
19170784Sjulian	node = LIST_FIRST(&ng_freenodes);
19270784Sjulian	if (node) {
19370784Sjulian		LIST_REMOVE(node, nd_nodes);
19470784Sjulian		bcopy(&node->nd_all, &temp, sizeof(temp));
19570784Sjulian		bzero(node, sizeof(struct ng_node));
19670784Sjulian		bcopy(&temp, &node->nd_all, sizeof(temp));
19770784Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
19870784Sjulian		node->nd_magic = ND_MAGIC;
19970784Sjulian	} else {
20070784Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
20170784Sjulian		_NG_ALLOC_NODE(node);
20270784Sjulian		if (node) {
20370784Sjulian			node->nd_magic = ND_MAGIC;
20470784Sjulian			mtx_enter(&ng_nodelist_mtx, MTX_DEF);
20570784Sjulian			SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all);
20670784Sjulian			mtx_exit(&ng_nodelist_mtx, MTX_DEF);
20770784Sjulian		}
20870784Sjulian	}
20970784Sjulian	return (node);
21070784Sjulian}
21170784Sjulian
21270784Sjulian#define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0)
21370784Sjulian#define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0)
21470784Sjulian
21570784Sjulian
21670784Sjulian#define NG_FREE_HOOK(hook)						\
21770784Sjulian	do {								\
21870784Sjulian		mtx_enter(&ng_nodelist_mtx, MTX_DEF);			\
21970784Sjulian		LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks);	\
22070784Sjulian		hook->hk_magic = 0;					\
22170784Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);			\
22270784Sjulian	} while (0)
22370784Sjulian
22470784Sjulian#define NG_FREE_NODE(node)						\
22570784Sjulian	do {								\
22670784Sjulian		mtx_enter(&ng_nodelist_mtx, MTX_DEF);			\
22770784Sjulian		LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes);	\
22870784Sjulian		node->nd_magic = 0;					\
22970784Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);			\
23070784Sjulian	} while (0)
23170784Sjulian
23270784Sjulian#else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
23370784Sjulian
23470784Sjulian#define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook)
23570784Sjulian#define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node)
23670784Sjulian
23770700Sjulian#define NG_FREE_HOOK(hook) do { FREE((hook), M_NETGRAPH_HOOK); } while (0)
23870700Sjulian#define NG_FREE_NODE(node) do { FREE((node), M_NETGRAPH_NODE); } while (0)
23970784Sjulian
24070784Sjulian#endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/
24170784Sjulian
24270700Sjulian/* Warning: Generally use NG_FREE_ITEM() instead */
24370700Sjulian#define NG_FREE_ITEM_REAL(item) do { FREE((item), M_NETGRAPH_ITEM); } while (0)
24470700Sjulian
24570700Sjulian
24652419Sjulian/* Set this to Debugger("X") to catch all errors as they occur */
24752419Sjulian#ifndef TRAP_ERROR
24852419Sjulian#define TRAP_ERROR
24952419Sjulian#endif
25052419Sjulian
25152722Sjulianstatic	ng_ID_t nextID = 1;
25252722Sjulian
25353403Sarchie#ifdef INVARIANTS
25453403Sarchie#define CHECK_DATA_MBUF(m)	do {					\
25553403Sarchie		struct mbuf *n;						\
25653403Sarchie		int total;						\
25753403Sarchie									\
25853403Sarchie		if (((m)->m_flags & M_PKTHDR) == 0)			\
25953403Sarchie			panic("%s: !PKTHDR", __FUNCTION__);		\
26053403Sarchie		for (total = 0, n = (m); n != NULL; n = n->m_next)	\
26153403Sarchie			total += n->m_len;				\
26253403Sarchie		if ((m)->m_pkthdr.len != total) {			\
26353403Sarchie			panic("%s: %d != %d",				\
26453403Sarchie			    __FUNCTION__, (m)->m_pkthdr.len, total);	\
26553403Sarchie		}							\
26653403Sarchie	} while (0)
26753403Sarchie#else
26853403Sarchie#define CHECK_DATA_MBUF(m)
26953403Sarchie#endif
27052722Sjulian
27153403Sarchie
27252419Sjulian/************************************************************************
27353913Sarchie	Parse type definitions for generic messages
27453913Sarchie************************************************************************/
27553913Sarchie
27653913Sarchie/* Handy structure parse type defining macro */
27753913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
27853913Sarchiestatic const struct ng_parse_struct_info				\
27953913Sarchie	ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args;	\
28053913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
28153913Sarchie	&ng_parse_struct_type,						\
28253913Sarchie	&ng_ ## lo ## _type_info					\
28353913Sarchie}
28453913Sarchie
28553913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
28653913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
28753913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
28853913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
28953913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
29053913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
29153913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
29253913Sarchie
29353913Sarchie/* Get length of an array when the length is stored as a 32 bit
29453913Sarchie   value immediately preceeding the array -- as with struct namelist
29553913Sarchie   and struct typelist. */
29653913Sarchiestatic int
29753913Sarchieng_generic_list_getLength(const struct ng_parse_type *type,
29853913Sarchie	const u_char *start, const u_char *buf)
29953913Sarchie{
30053913Sarchie	return *((const u_int32_t *)(buf - 4));
30153913Sarchie}
30253913Sarchie
30353913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */
30453913Sarchiestatic int
30553913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type,
30653913Sarchie	const u_char *start, const u_char *buf)
30753913Sarchie{
30853913Sarchie	const struct hooklist *hl = (const struct hooklist *)start;
30953913Sarchie
31053913Sarchie	return hl->nodeinfo.hooks;
31153913Sarchie}
31253913Sarchie
31353913Sarchie/* Array type for a variable length array of struct namelist */
31453913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
31553913Sarchie	&ng_generic_nodeinfo_type,
31653913Sarchie	&ng_generic_list_getLength
31753913Sarchie};
31853913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = {
31953913Sarchie	&ng_parse_array_type,
32053913Sarchie	&ng_nodeinfoarray_type_info
32153913Sarchie};
32253913Sarchie
32353913Sarchie/* Array type for a variable length array of struct typelist */
32453913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = {
32553913Sarchie	&ng_generic_typeinfo_type,
32653913Sarchie	&ng_generic_list_getLength
32753913Sarchie};
32853913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = {
32953913Sarchie	&ng_parse_array_type,
33053913Sarchie	&ng_typeinfoarray_type_info
33153913Sarchie};
33253913Sarchie
33353913Sarchie/* Array type for array of struct linkinfo in struct hooklist */
33453913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
33553913Sarchie	&ng_generic_linkinfo_type,
33653913Sarchie	&ng_generic_linkinfo_getLength
33753913Sarchie};
33853913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = {
33953913Sarchie	&ng_parse_array_type,
34053913Sarchie	&ng_generic_linkinfo_array_type_info
34153913Sarchie};
34253913Sarchie
34353913SarchieDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
34453913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
34553913Sarchie	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
34653913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
34753913Sarchie	(&ng_generic_nodeinfoarray_type));
34853913Sarchie
34953913Sarchie/* List of commands and how to convert arguments to/from ASCII */
35053913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = {
35153913Sarchie	{
35253913Sarchie	  NGM_GENERIC_COOKIE,
35353913Sarchie	  NGM_SHUTDOWN,
35453913Sarchie	  "shutdown",
35553913Sarchie	  NULL,
35653913Sarchie	  NULL
35753913Sarchie	},
35853913Sarchie	{
35953913Sarchie	  NGM_GENERIC_COOKIE,
36053913Sarchie	  NGM_MKPEER,
36153913Sarchie	  "mkpeer",
36253913Sarchie	  &ng_generic_mkpeer_type,
36353913Sarchie	  NULL
36453913Sarchie	},
36553913Sarchie	{
36653913Sarchie	  NGM_GENERIC_COOKIE,
36753913Sarchie	  NGM_CONNECT,
36853913Sarchie	  "connect",
36953913Sarchie	  &ng_generic_connect_type,
37053913Sarchie	  NULL
37153913Sarchie	},
37253913Sarchie	{
37353913Sarchie	  NGM_GENERIC_COOKIE,
37453913Sarchie	  NGM_NAME,
37553913Sarchie	  "name",
37653913Sarchie	  &ng_generic_name_type,
37753913Sarchie	  NULL
37853913Sarchie	},
37953913Sarchie	{
38053913Sarchie	  NGM_GENERIC_COOKIE,
38153913Sarchie	  NGM_RMHOOK,
38253913Sarchie	  "rmhook",
38353913Sarchie	  &ng_generic_rmhook_type,
38453913Sarchie	  NULL
38553913Sarchie	},
38653913Sarchie	{
38753913Sarchie	  NGM_GENERIC_COOKIE,
38853913Sarchie	  NGM_NODEINFO,
38953913Sarchie	  "nodeinfo",
39053913Sarchie	  NULL,
39153913Sarchie	  &ng_generic_nodeinfo_type
39253913Sarchie	},
39353913Sarchie	{
39453913Sarchie	  NGM_GENERIC_COOKIE,
39553913Sarchie	  NGM_LISTHOOKS,
39653913Sarchie	  "listhooks",
39753913Sarchie	  NULL,
39853913Sarchie	  &ng_generic_hooklist_type
39953913Sarchie	},
40053913Sarchie	{
40153913Sarchie	  NGM_GENERIC_COOKIE,
40253913Sarchie	  NGM_LISTNAMES,
40353913Sarchie	  "listnames",
40453913Sarchie	  NULL,
40553913Sarchie	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
40653913Sarchie	},
40753913Sarchie	{
40853913Sarchie	  NGM_GENERIC_COOKIE,
40953913Sarchie	  NGM_LISTNODES,
41053913Sarchie	  "listnodes",
41153913Sarchie	  NULL,
41253913Sarchie	  &ng_generic_listnodes_type
41353913Sarchie	},
41453913Sarchie	{
41553913Sarchie	  NGM_GENERIC_COOKIE,
41653913Sarchie	  NGM_LISTTYPES,
41753913Sarchie	  "listtypes",
41853913Sarchie	  NULL,
41953913Sarchie	  &ng_generic_typeinfo_type
42053913Sarchie	},
42153913Sarchie	{
42253913Sarchie	  NGM_GENERIC_COOKIE,
42362471Sphk	  NGM_TEXT_CONFIG,
42462471Sphk	  "textconfig",
42562471Sphk	  NULL,
42662471Sphk	  &ng_parse_string_type
42762471Sphk	},
42862471Sphk	{
42962471Sphk	  NGM_GENERIC_COOKIE,
43053913Sarchie	  NGM_TEXT_STATUS,
43153913Sarchie	  "textstatus",
43253913Sarchie	  NULL,
43353913Sarchie	  &ng_parse_string_type
43453913Sarchie	},
43553913Sarchie	{
43653913Sarchie	  NGM_GENERIC_COOKIE,
43753913Sarchie	  NGM_ASCII2BINARY,
43853913Sarchie	  "ascii2binary",
43953913Sarchie	  &ng_parse_ng_mesg_type,
44053913Sarchie	  &ng_parse_ng_mesg_type
44153913Sarchie	},
44253913Sarchie	{
44353913Sarchie	  NGM_GENERIC_COOKIE,
44453913Sarchie	  NGM_BINARY2ASCII,
44553913Sarchie	  "binary2ascii",
44653913Sarchie	  &ng_parse_ng_mesg_type,
44753913Sarchie	  &ng_parse_ng_mesg_type
44853913Sarchie	},
44953913Sarchie	{ 0 }
45053913Sarchie};
45153913Sarchie
45253913Sarchie/************************************************************************
45352419Sjulian			Node routines
45452419Sjulian************************************************************************/
45552419Sjulian
45652419Sjulian/*
45752419Sjulian * Instantiate a node of the requested type
45852419Sjulian */
45952419Sjulianint
46052419Sjulianng_make_node(const char *typename, node_p *nodepp)
46152419Sjulian{
46252419Sjulian	struct ng_type *type;
46370700Sjulian	int	error;
46452419Sjulian
46552419Sjulian	/* Check that the type makes sense */
46652419Sjulian	if (typename == NULL) {
46752419Sjulian		TRAP_ERROR;
46852419Sjulian		return (EINVAL);
46952419Sjulian	}
47052419Sjulian
47152419Sjulian	/* Locate the node type */
47252419Sjulian	if ((type = ng_findtype(typename)) == NULL) {
47359875Speter		char filename[NG_TYPELEN + 4];
47452419Sjulian		linker_file_t lf;
47552419Sjulian		int error;
47652419Sjulian
47752419Sjulian		/* Not found, try to load it as a loadable module */
47869923Sjulian		snprintf(filename, sizeof(filename), "ng_%s", typename);
47959875Speter		error = linker_load_file(filename, &lf);
48052419Sjulian		if (error != 0)
48152419Sjulian			return (error);
48252419Sjulian		lf->userrefs++;		/* pretend loaded by the syscall */
48352419Sjulian
48452419Sjulian		/* Try again, as now the type should have linked itself in */
48552419Sjulian		if ((type = ng_findtype(typename)) == NULL)
48652419Sjulian			return (ENXIO);
48752419Sjulian	}
48852419Sjulian
48970700Sjulian	/*
49070700Sjulian	 * If we have a constructor, then make the node and
49170700Sjulian	 * call the constructor to do type specific initialisation.
49270700Sjulian	 */
49370700Sjulian	if (type->constructor != NULL) {
49470700Sjulian		if ((error = ng_make_node_common(type, nodepp)) == 0) {
49570700Sjulian			if ((error = ((*type->constructor)(*nodepp)) != 0)) {
49670784Sjulian				NG_NODE_UNREF(*nodepp);
49770700Sjulian			}
49870700Sjulian		}
49970700Sjulian	} else {
50070700Sjulian		/*
50170700Sjulian		 * Node has no constructor. We cannot ask for one
50270700Sjulian		 * to be made. It must be brought into existance by
50370700Sjulian		 * some external agency. The external acency should
50470700Sjulian		 * call ng_make_node_common() directly to get the
50570700Sjulian		 * netgraph part initialised.
50670700Sjulian		 */
50770784Sjulian		TRAP_ERROR
50870700Sjulian		error = EINVAL;
50970700Sjulian	}
51070700Sjulian	return (error);
51152419Sjulian}
51252419Sjulian
51352419Sjulian/*
51470700Sjulian * Generic node creation. Called by node initialisation for externally
51570700Sjulian * instantiated nodes (e.g. hardware, sockets, etc ).
51652419Sjulian * The returned node has a reference count of 1.
51752419Sjulian */
51852419Sjulianint
51952419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp)
52052419Sjulian{
52152419Sjulian	node_p node;
52252419Sjulian
52352419Sjulian	/* Require the node type to have been already installed */
52452419Sjulian	if (ng_findtype(type->name) == NULL) {
52552419Sjulian		TRAP_ERROR;
52652419Sjulian		return (EINVAL);
52752419Sjulian	}
52852419Sjulian
52952419Sjulian	/* Make a node and try attach it to the type */
53070784Sjulian	NG_ALLOC_NODE(node);
53152419Sjulian	if (node == NULL) {
53252419Sjulian		TRAP_ERROR;
53352419Sjulian		return (ENOMEM);
53452419Sjulian	}
53570784Sjulian	node->nd_type = type;
53670784Sjulian	NG_NODE_REF(node);				/* note reference */
53752419Sjulian	type->refs++;
53852419Sjulian
53970784Sjulian	mtx_init(&node->nd_input_queue.q_mtx, "netgraph node mutex", 0);
54070784Sjulian	node->nd_input_queue.queue = NULL;
54170784Sjulian	node->nd_input_queue.last = &node->nd_input_queue.queue;
54270784Sjulian	node->nd_input_queue.q_flags = 0;
54370784Sjulian	node->nd_input_queue.q_node = node;
54452419Sjulian
54552419Sjulian	/* Initialize hook list for new node */
54670784Sjulian	LIST_INIT(&node->nd_hooks);
54752419Sjulian
54870700Sjulian	/* Link us into the node linked list */
54970700Sjulian	mtx_enter(&ng_nodelist_mtx, MTX_DEF);
55070784Sjulian	LIST_INSERT_HEAD(&ng_nodelist, node, nd_nodes);
55170700Sjulian	mtx_exit(&ng_nodelist_mtx, MTX_DEF);
55270700Sjulian
55370700Sjulian
55452722Sjulian	/* get an ID and put us in the hash chain */
55570700Sjulian	mtx_enter(&ng_idhash_mtx, MTX_DEF);
55670784Sjulian	for (;;) { /* wrap protection, even if silly */
55770700Sjulian		node_p node2 = NULL;
55870784Sjulian		node->nd_ID = nextID++; /* 137/second for 1 year before wrap */
55970784Sjulian		/* Is there a problem with the new number? */
56070784Sjulian		if ((node->nd_ID == 0)
56170784Sjulian		|| (node2 = ng_ID2noderef(node->nd_ID))) {
56270700Sjulian			if (node2) {
56370784Sjulian				NG_NODE_UNREF(node2);
56470700Sjulian				node2 = NULL;
56570700Sjulian			}
56670784Sjulian		} else {
56770784Sjulian			break;
56870700Sjulian		}
56970784Sjulian	}
57070784Sjulian	LIST_INSERT_HEAD(&ng_ID_hash[node->nd_ID % ID_HASH_SIZE],
57170784Sjulian							node, nd_idnodes);
57270700Sjulian	mtx_exit(&ng_idhash_mtx, MTX_DEF);
57352722Sjulian
57452419Sjulian	/* Done */
57552419Sjulian	*nodepp = node;
57652419Sjulian	return (0);
57752419Sjulian}
57852419Sjulian
57952419Sjulian/*
58052419Sjulian * Forceably start the shutdown process on a node. Either call
58152419Sjulian * it's shutdown method, or do the default shutdown if there is
58252419Sjulian * no type-specific method.
58352419Sjulian *
58470700Sjulian * We can only be called form a shutdown message, so we know we have
58570700Sjulian * a writer lock, and therefore exclusive access.
58670700Sjulian *
58770700Sjulian * Persistent node types must have a type-specific method which
58870700Sjulian * Allocates a new node. This one is irretrievably going away.
58952419Sjulian */
59052419Sjulianvoid
59152419Sjulianng_rmnode(node_p node)
59252419Sjulian{
59352419Sjulian	/* Check if it's already shutting down */
59470784Sjulian	if ((node->nd_flags & NG_CLOSING) != 0)
59552419Sjulian		return;
59652419Sjulian
59752419Sjulian	/* Add an extra reference so it doesn't go away during this */
59870784Sjulian	NG_NODE_REF(node);
59952419Sjulian
60070784Sjulian	/*
60170784Sjulian	 * Mark it invalid so any newcomers know not to try use it
60270784Sjulian	 * Also add our own mark so we can't recurse
60370784Sjulian	 * note that NG_INVALID does not do this as it's also set during
60470784Sjulian	 * creation
60570784Sjulian	 */
60670784Sjulian	node->nd_flags |= NG_INVALID|NG_CLOSING;
60752419Sjulian
60870700Sjulian	ng_cutlinks(node);
60970784Sjulian
61070700Sjulian	/*
61170700Sjulian	 * Drain the input queue forceably.
61270784Sjulian	 * it has no hooks so what's it going to do, bleed on someone?
61370784Sjulian	 * Theoretically we came here from a queue entry that was added
61470784Sjulian	 * Just before the queue was closed, so it should be empty anyway.
61570700Sjulian	 */
61670784Sjulian	ng_flush_input_queue(&node->nd_input_queue);
61770700Sjulian
61870700Sjulian	/*
61970700Sjulian	 * Take us off the work queue if we are there.
62070784Sjulian	 * We definatly have no work to be done.
62170700Sjulian	 */
62270700Sjulian	ng_worklist_remove(node);
62370700Sjulian
62452419Sjulian	/* Ask the type if it has anything to do in this case */
62570784Sjulian	if (node->nd_type && node->nd_type->shutdown) {
62670784Sjulian		(*node->nd_type->shutdown)(node);
62770700Sjulian	} else {				/* do the default thing */
62870784Sjulian		NG_NODE_UNREF(node);
62952419Sjulian	}
63070784Sjulian	if (NG_NODE_IS_VALID(node)) {
63170784Sjulian		/*
63270784Sjulian		 * Well, blow me down if the node code hasn't declared
63370784Sjulian		 * that it doesn't want to die.
63470784Sjulian		 * Presumably it is a persistant node.
63570784Sjulian		 * XXX we need a way to tell the node
63670784Sjulian		 * "No, really.. the hardware's going away.. REALLY die"
63770784Sjulian		 * We need a way
63870784Sjulian		 */
63970784Sjulian		return;
64070784Sjulian	}
64152419Sjulian
64270784Sjulian	ng_unname(node); /* basically a NOP these days */
64370784Sjulian
64470784Sjulian	/*
64570784Sjulian	 * Remove extra reference, possibly the last
64670784Sjulian	 * Possible other holders of references may include
64770784Sjulian	 * timeout callouts, but theoretically the node's supposed to
64870784Sjulian	 * have cancelled them. Possibly hardware dependencies may
64970784Sjulian	 * force a driver to 'linger' with a reference.
65070784Sjulian	 */
65170784Sjulian	NG_NODE_UNREF(node);
65252419Sjulian}
65352419Sjulian
65452419Sjulian/*
65552419Sjulian * Called by the destructor to remove any STANDARD external references
65670784Sjulian * May one day have it's own message to call it..
65752419Sjulian */
65852419Sjulianvoid
65952419Sjulianng_cutlinks(node_p node)
66052419Sjulian{
66152419Sjulian	hook_p  hook;
66252419Sjulian
66352419Sjulian	/* Make sure that this is set to stop infinite loops */
66470784Sjulian	node->nd_flags |= NG_INVALID;
66552419Sjulian
66670700Sjulian	/*
66770700Sjulian	 * Drain the input queue forceably.
66870700Sjulian	 * We also do this in ng_rmnode
66970700Sjulian	 * to make sure we get all code paths.
67070700Sjulian	 */
67170784Sjulian	ng_flush_input_queue(&node->nd_input_queue);
67252419Sjulian
67352419Sjulian	/* Notify all remaining connected nodes to disconnect */
67470784Sjulian	while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL)
67552419Sjulian		ng_destroy_hook(hook);
67652419Sjulian}
67752419Sjulian
67852419Sjulian/*
67952419Sjulian * Remove a reference to the node, possibly the last
68052419Sjulian */
68152419Sjulianvoid
68270784Sjulianng_unref_node(node_p node)
68352419Sjulian{
68470784Sjulian	int     v;
68570784Sjulian	do {
68670784Sjulian		v = node->nd_refs;
68770784Sjulian	} while (! atomic_cmpset_int(&node->nd_refs, v, v - 1));
68869519Sjulian
68970784Sjulian	if (v == 1) { /* we were the last */
69070700Sjulian
69170700Sjulian		mtx_enter(&ng_nodelist_mtx, MTX_DEF);
69270784Sjulian		node->nd_type->refs--; /* XXX maybe should get types lock? */
69370784Sjulian		LIST_REMOVE(node, nd_nodes);
69470700Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
69570700Sjulian
69670700Sjulian		mtx_enter(&ng_idhash_mtx, MTX_DEF);
69770784Sjulian		LIST_REMOVE(node, nd_idnodes);
69870700Sjulian		mtx_exit(&ng_idhash_mtx, MTX_DEF);
69952419Sjulian
70070791Sjulian		mtx_destroy(&node->nd_input_queue.q_mtx);
70170700Sjulian		NG_FREE_NODE(node);
70252419Sjulian	}
70352419Sjulian}
70452419Sjulian
70552419Sjulian/************************************************************************
70652722Sjulian			Node ID handling
70752722Sjulian************************************************************************/
70852722Sjulianstatic node_p
70970700Sjulianng_ID2noderef(ng_ID_t ID)
71052722Sjulian{
71170784Sjulian	node_p node;
71270700Sjulian	mtx_enter(&ng_idhash_mtx, MTX_DEF);
71370784Sjulian	LIST_FOREACH(node, &ng_ID_hash[ID % ID_HASH_SIZE], nd_idnodes) {
71470912Sjulian		if (NG_NODE_IS_VALID(node) && (NG_NODE_ID(node) == ID)) {
71552722Sjulian			break;
71670912Sjulian		}
71752722Sjulian	}
71870784Sjulian	if(node)
71970784Sjulian		NG_NODE_REF(node);
72070700Sjulian	mtx_exit(&ng_idhash_mtx, MTX_DEF);
72170784Sjulian	return(node);
72252722Sjulian}
72352722Sjulian
72452722Sjulianng_ID_t
72552722Sjulianng_node2ID(node_p node)
72652722Sjulian{
72770912Sjulian	return (node ? NG_NODE_ID(node) : 0);
72852722Sjulian}
72952722Sjulian
73052722Sjulian/************************************************************************
73152419Sjulian			Node name handling
73252419Sjulian************************************************************************/
73352419Sjulian
73452419Sjulian/*
73552419Sjulian * Assign a node a name. Once assigned, the name cannot be changed.
73652419Sjulian */
73752419Sjulianint
73852419Sjulianng_name_node(node_p node, const char *name)
73952419Sjulian{
74052419Sjulian	int i;
74170700Sjulian	node_p node2;
74252419Sjulian
74352419Sjulian	/* Check the name is valid */
74452419Sjulian	for (i = 0; i < NG_NODELEN + 1; i++) {
74552419Sjulian		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
74652419Sjulian			break;
74752419Sjulian	}
74852419Sjulian	if (i == 0 || name[i] != '\0') {
74952419Sjulian		TRAP_ERROR;
75052419Sjulian		return (EINVAL);
75152419Sjulian	}
75252722Sjulian	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
75352419Sjulian		TRAP_ERROR;
75452419Sjulian		return (EINVAL);
75552419Sjulian	}
75652419Sjulian
75752419Sjulian	/* Check the name isn't already being used */
75870700Sjulian	if ((node2 = ng_name2noderef(node, name)) != NULL) {
75970784Sjulian		NG_NODE_UNREF(node2);
76052419Sjulian		TRAP_ERROR;
76152419Sjulian		return (EADDRINUSE);
76252419Sjulian	}
76352419Sjulian
76470700Sjulian	/* copy it */
76570784Sjulian	strncpy(NG_NODE_NAME(node), name, NG_NODELEN);
76652419Sjulian
76752419Sjulian	return (0);
76852419Sjulian}
76952419Sjulian
77052419Sjulian/*
77152419Sjulian * Find a node by absolute name. The name should NOT end with ':'
77252419Sjulian * The name "." means "this node" and "[xxx]" means "the node
77352419Sjulian * with ID (ie, at address) xxx".
77452419Sjulian *
77552419Sjulian * Returns the node if found, else NULL.
77670700Sjulian * Eventually should add something faster than a sequential search.
77770784Sjulian * Note it aquires a reference on the node so you can be sure it's still there.
77852419Sjulian */
77952419Sjuliannode_p
78070700Sjulianng_name2noderef(node_p here, const char *name)
78152419Sjulian{
78252722Sjulian	node_p node;
78352722Sjulian	ng_ID_t temp;
78452419Sjulian
78552419Sjulian	/* "." means "this node" */
78670700Sjulian	if (strcmp(name, ".") == 0) {
78770784Sjulian		NG_NODE_REF(here);
78870700Sjulian		return(here);
78970700Sjulian	}
79052419Sjulian
79152419Sjulian	/* Check for name-by-ID */
79252722Sjulian	if ((temp = ng_decodeidname(name)) != 0) {
79370700Sjulian		return (ng_ID2noderef(temp));
79452419Sjulian	}
79552419Sjulian
79652419Sjulian	/* Find node by name */
79770700Sjulian	mtx_enter(&ng_nodelist_mtx, MTX_DEF);
79870784Sjulian	LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
79970912Sjulian		if (NG_NODE_IS_VALID(node)
80070912Sjulian		&& NG_NODE_HAS_NAME(node)
80170912Sjulian		&& (strcmp(NG_NODE_NAME(node), name) == 0)) {
80252419Sjulian			break;
80370912Sjulian		}
80452419Sjulian	}
80570700Sjulian	if (node)
80670784Sjulian		NG_NODE_REF(node);
80770700Sjulian	mtx_exit(&ng_nodelist_mtx, MTX_DEF);
80852419Sjulian	return (node);
80952419Sjulian}
81052419Sjulian
81152419Sjulian/*
81252722Sjulian * Decode a ID name, eg. "[f03034de]". Returns 0 if the
81352722Sjulian * string is not valid, otherwise returns the value.
81452419Sjulian */
81552722Sjulianstatic ng_ID_t
81652419Sjulianng_decodeidname(const char *name)
81752419Sjulian{
81852816Sarchie	const int len = strlen(name);
81953648Sarchie	char *eptr;
82052816Sarchie	u_long val;
82152419Sjulian
82252816Sarchie	/* Check for proper length, brackets, no leading junk */
82370912Sjulian	if ((len < 3)
82470912Sjulian	|| (name[0] != '[')
82570912Sjulian	|| (name[len - 1] != ']')
82670912Sjulian	|| (!isxdigit(name[1]))) {
82770912Sjulian		return ((ng_ID_t)0);
82870912Sjulian	}
82952419Sjulian
83052816Sarchie	/* Decode number */
83152816Sarchie	val = strtoul(name + 1, &eptr, 16);
83270912Sjulian	if ((eptr - name != len - 1)
83370912Sjulian	|| (val == ULONG_MAX)
83470912Sjulian	|| (val == 0)) {
83553042Sjulian		return ((ng_ID_t)0);
83670912Sjulian	}
83752816Sarchie	return (ng_ID_t)val;
83852419Sjulian}
83952419Sjulian
84052419Sjulian/*
84152419Sjulian * Remove a name from a node. This should only be called
84252419Sjulian * when shutting down and removing the node.
84352419Sjulian */
84452419Sjulianvoid
84552419Sjulianng_unname(node_p node)
84652419Sjulian{
84770784Sjulian	bzero(NG_NODE_NAME(node), NG_NODELEN);
84852419Sjulian}
84952419Sjulian
85052419Sjulian/************************************************************************
85152419Sjulian			Hook routines
85252419Sjulian Names are not optional. Hooks are always connected, except for a
85352419Sjulian brief moment within these routines.
85452419Sjulian************************************************************************/
85552419Sjulian
85652419Sjulian/*
85752419Sjulian * Remove a hook reference
85852419Sjulian */
85970784Sjulianvoid
86052419Sjulianng_unref_hook(hook_p hook)
86152419Sjulian{
86270784Sjulian	int     v;
86370784Sjulian	do {
86470784Sjulian		v = hook->hk_refs;
86570784Sjulian	} while (! atomic_cmpset_int(&hook->hk_refs, v, v - 1));
86669519Sjulian
86770784Sjulian	if (v == 1) { /* we were the last */
86870784Sjulian		if (NG_HOOK_NODE(hook)) {
86970784Sjulian			NG_NODE_UNREF((NG_HOOK_NODE(hook)));
87070784Sjulian			hook->hk_node = NULL;
87170700Sjulian		}
87270700Sjulian		NG_FREE_HOOK(hook);
87370700Sjulian	}
87452419Sjulian}
87552419Sjulian
87652419Sjulian/*
87752419Sjulian * Add an unconnected hook to a node. Only used internally.
87852419Sjulian */
87952419Sjulianstatic int
88052419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp)
88152419Sjulian{
88252419Sjulian	hook_p hook;
88352419Sjulian	int error = 0;
88452419Sjulian
88552419Sjulian	/* Check that the given name is good */
88652419Sjulian	if (name == NULL) {
88752419Sjulian		TRAP_ERROR;
88852419Sjulian		return (EINVAL);
88952419Sjulian	}
89054096Sarchie	if (ng_findhook(node, name) != NULL) {
89154096Sarchie		TRAP_ERROR;
89254096Sarchie		return (EEXIST);
89352419Sjulian	}
89452419Sjulian
89552419Sjulian	/* Allocate the hook and link it up */
89670784Sjulian	NG_ALLOC_HOOK(hook);
89752419Sjulian	if (hook == NULL) {
89852419Sjulian		TRAP_ERROR;
89952419Sjulian		return (ENOMEM);
90052419Sjulian	}
90170784Sjulian	hook->hk_refs = 1;
90270784Sjulian	hook->hk_flags = HK_INVALID;
90370784Sjulian	hook->hk_node = node;
90470784Sjulian	NG_NODE_REF(node);		/* each hook counts as a reference */
90552419Sjulian
90652419Sjulian	/* Check if the node type code has something to say about it */
90770784Sjulian	if (node->nd_type->newhook != NULL)
90870784Sjulian		if ((error = (*node->nd_type->newhook)(node, hook, name)) != 0) {
90970784Sjulian			NG_HOOK_UNREF(hook);	/* this frees the hook */
91070700Sjulian			return (error);
91170700Sjulian		}
91252419Sjulian
91352419Sjulian	/*
91452419Sjulian	 * The 'type' agrees so far, so go ahead and link it in.
91552419Sjulian	 * We'll ask again later when we actually connect the hooks.
91670700Sjulian	 * The reference we have is for this linkage.
91752419Sjulian	 */
91870784Sjulian	LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks);
91970784Sjulian	node->nd_numhooks++;
92052419Sjulian
92152419Sjulian	/* Set hook name */
92270784Sjulian	strncpy(NG_HOOK_NAME(hook), name, NG_HOOKLEN);
92352419Sjulian	if (hookp)
92452419Sjulian		*hookp = hook;
92552419Sjulian	return (error);
92652419Sjulian}
92752419Sjulian
92852419Sjulian/*
92952419Sjulian * Connect a pair of hooks. Only used internally.
93052419Sjulian */
93152419Sjulianstatic int
93252419Sjulianng_connect(hook_p hook1, hook_p hook2)
93352419Sjulian{
93452419Sjulian	int     error;
93552419Sjulian
93670784Sjulian	hook1->hk_peer = hook2;
93770784Sjulian	hook2->hk_peer = hook1;
93852419Sjulian
93952419Sjulian	/* Give each node the opportunity to veto the impending connection */
94070784Sjulian	if (hook1->hk_node->nd_type->connect) {
94170784Sjulian		if ((error = (*hook1->hk_node->nd_type->connect) (hook1))) {
94252419Sjulian			ng_destroy_hook(hook1);	/* also zaps hook2 */
94352419Sjulian			return (error);
94452419Sjulian		}
94552419Sjulian	}
94670784Sjulian	if (hook2->hk_node->nd_type->connect) {
94770784Sjulian		if ((error = (*hook2->hk_node->nd_type->connect) (hook2))) {
94852419Sjulian			ng_destroy_hook(hook2);	/* also zaps hook1 */
94952419Sjulian			return (error);
95052419Sjulian		}
95152419Sjulian	}
95270784Sjulian	hook1->hk_flags &= ~HK_INVALID;
95370784Sjulian	hook2->hk_flags &= ~HK_INVALID;
95452419Sjulian	return (0);
95552419Sjulian}
95652419Sjulian
95752419Sjulian/*
95854096Sarchie * Find a hook
95954096Sarchie *
96054096Sarchie * Node types may supply their own optimized routines for finding
96154096Sarchie * hooks.  If none is supplied, we just do a linear search.
96254096Sarchie */
96354096Sarchiehook_p
96454096Sarchieng_findhook(node_p node, const char *name)
96554096Sarchie{
96654096Sarchie	hook_p hook;
96754096Sarchie
96870784Sjulian	if (node->nd_type->findhook != NULL)
96970784Sjulian		return (*node->nd_type->findhook)(node, name);
97070784Sjulian	LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
97170912Sjulian		if (NG_HOOK_IS_VALID(hook)
97270912Sjulian		&& (strcmp(NG_HOOK_NAME(hook), name) == 0)) {
97354096Sarchie			return (hook);
97454096Sarchie	}
97554096Sarchie	return (NULL);
97654096Sarchie}
97754096Sarchie
97854096Sarchie/*
97952419Sjulian * Destroy a hook
98052419Sjulian *
98152419Sjulian * As hooks are always attached, this really destroys two hooks.
98252419Sjulian * The one given, and the one attached to it. Disconnect the hooks
98352419Sjulian * from each other first.
98452419Sjulian */
98552419Sjulianvoid
98652419Sjulianng_destroy_hook(hook_p hook)
98752419Sjulian{
98870784Sjulian	hook_p peer = NG_HOOK_PEER(hook);
98952419Sjulian
99070784Sjulian	hook->hk_flags |= HK_INVALID;		/* as soon as possible */
99152419Sjulian	if (peer) {
99270784Sjulian		peer->hk_flags |= HK_INVALID;	/* as soon as possible */
99370784Sjulian		hook->hk_peer = NULL;
99470784Sjulian		peer->hk_peer = NULL;
99552419Sjulian		ng_disconnect_hook(peer);
99652419Sjulian	}
99752419Sjulian	ng_disconnect_hook(hook);
99852419Sjulian}
99952419Sjulian
100052419Sjulian/*
100152419Sjulian * Notify the node of the hook's demise. This may result in more actions
100252419Sjulian * (e.g. shutdown) but we don't do that ourselves and don't know what
100352419Sjulian * happens there. If there is no appropriate handler, then just remove it
100452419Sjulian * (and decrement the reference count of it's node which in turn might
100552419Sjulian * make something happen).
100652419Sjulian */
100752419Sjulianstatic void
100852419Sjulianng_disconnect_hook(hook_p hook)
100952419Sjulian{
101070784Sjulian	node_p node = NG_HOOK_NODE(hook);
101152419Sjulian
101252419Sjulian	/*
101352419Sjulian	 * Remove the hook from the node's list to avoid possible recursion
101452419Sjulian	 * in case the disconnection results in node shutdown.
101552419Sjulian	 */
101670784Sjulian	LIST_REMOVE(hook, hk_hooks);
101770784Sjulian	node->nd_numhooks--;
101870784Sjulian	if (node->nd_type->disconnect) {
101952419Sjulian		/*
102052419Sjulian		 * The type handler may elect to destroy the peer so don't
102152419Sjulian		 * trust its existance after this point.
102252419Sjulian		 */
102370784Sjulian		(*node->nd_type->disconnect) (hook);
102452419Sjulian	}
102570784Sjulian	NG_HOOK_UNREF(hook);
102652419Sjulian}
102752419Sjulian
102852419Sjulian/*
102952419Sjulian * Take two hooks on a node and merge the connection so that the given node
103052419Sjulian * is effectively bypassed.
103152419Sjulian */
103252419Sjulianint
103352419Sjulianng_bypass(hook_p hook1, hook_p hook2)
103452419Sjulian{
103570784Sjulian	if (hook1->hk_node != hook2->hk_node) {
103670784Sjulian		TRAP_ERROR
103752419Sjulian		return (EINVAL);
103870784Sjulian	}
103970784Sjulian	hook1->hk_peer->hk_peer = hook2->hk_peer;
104070784Sjulian	hook2->hk_peer->hk_peer = hook1->hk_peer;
104152419Sjulian
104252419Sjulian	/* XXX If we ever cache methods on hooks update them as well */
104370784Sjulian	hook1->hk_peer = NULL;
104470784Sjulian	hook2->hk_peer = NULL;
104552419Sjulian	ng_destroy_hook(hook1);
104652419Sjulian	ng_destroy_hook(hook2);
104752419Sjulian	return (0);
104852419Sjulian}
104952419Sjulian
105052419Sjulian/*
105152419Sjulian * Install a new netgraph type
105252419Sjulian */
105352419Sjulianint
105452419Sjulianng_newtype(struct ng_type *tp)
105552419Sjulian{
105652419Sjulian	const size_t namelen = strlen(tp->name);
105752419Sjulian
105852419Sjulian	/* Check version and type name fields */
105970159Sjulian	if ((tp->version != NG_ABI_VERSION)
106070159Sjulian	|| (namelen == 0)
106170159Sjulian	|| (namelen > NG_TYPELEN)) {
106252419Sjulian		TRAP_ERROR;
106352419Sjulian		return (EINVAL);
106452419Sjulian	}
106552419Sjulian
106652419Sjulian	/* Check for name collision */
106752419Sjulian	if (ng_findtype(tp->name) != NULL) {
106852419Sjulian		TRAP_ERROR;
106952419Sjulian		return (EEXIST);
107052419Sjulian	}
107152419Sjulian
107270700Sjulian	tp->refs = 0;
107370700Sjulian
107452419Sjulian	/* Link in new type */
107570700Sjulian	mtx_enter(&ng_typelist_mtx, MTX_DEF);
107670700Sjulian	LIST_INSERT_HEAD(&ng_typelist, tp, types);
107770700Sjulian	mtx_exit(&ng_typelist_mtx, MTX_DEF);
107852419Sjulian	return (0);
107952419Sjulian}
108052419Sjulian
108152419Sjulian/*
108252419Sjulian * Look for a type of the name given
108352419Sjulian */
108452419Sjulianstruct ng_type *
108552419Sjulianng_findtype(const char *typename)
108652419Sjulian{
108752419Sjulian	struct ng_type *type;
108852419Sjulian
108970700Sjulian	mtx_enter(&ng_typelist_mtx, MTX_DEF);
109070700Sjulian	LIST_FOREACH(type, &ng_typelist, types) {
109152419Sjulian		if (strcmp(type->name, typename) == 0)
109252419Sjulian			break;
109352419Sjulian	}
109470700Sjulian	mtx_exit(&ng_typelist_mtx, MTX_DEF);
109552419Sjulian	return (type);
109652419Sjulian}
109752419Sjulian
109852419Sjulian/************************************************************************
109952419Sjulian			Composite routines
110052419Sjulian************************************************************************/
110152419Sjulian
110252419Sjulian/*
110352419Sjulian * Make a peer and connect. The order is arranged to minimise
110452419Sjulian * the work needed to back out in case of error.
110552419Sjulian */
110652419Sjulianint
110752419Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type)
110852419Sjulian{
110952419Sjulian	node_p  node2;
111052419Sjulian	hook_p  hook;
111152419Sjulian	hook_p  hook2;
111252419Sjulian	int     error;
111352419Sjulian
111452419Sjulian	if ((error = ng_add_hook(node, name, &hook)))
111552419Sjulian		return (error);
111652419Sjulian	if ((error = ng_make_node(type, &node2))) {
111752419Sjulian		ng_destroy_hook(hook);
111852419Sjulian		return (error);
111952419Sjulian	}
112052419Sjulian	if ((error = ng_add_hook(node2, name2, &hook2))) {
112152419Sjulian		ng_rmnode(node2);
112252419Sjulian		ng_destroy_hook(hook);
112352419Sjulian		return (error);
112452419Sjulian	}
112552419Sjulian
112652419Sjulian	/*
112752419Sjulian	 * Actually link the two hooks together.. on failure they are
112852419Sjulian	 * destroyed so we don't have to do that here.
112952419Sjulian	 */
113052419Sjulian	if ((error = ng_connect(hook, hook2)))
113152419Sjulian		ng_rmnode(node2);
113252419Sjulian	return (error);
113352419Sjulian}
113452419Sjulian
113552419Sjulian/*
113652419Sjulian * Connect two nodes using the specified hooks
113752419Sjulian */
113852419Sjulianint
113952419Sjulianng_con_nodes(node_p node, const char *name, node_p node2, const char *name2)
114052419Sjulian{
114152419Sjulian	int     error;
114252419Sjulian	hook_p  hook;
114352419Sjulian	hook_p  hook2;
114452419Sjulian
114552419Sjulian	if ((error = ng_add_hook(node, name, &hook)))
114652419Sjulian		return (error);
114752419Sjulian	if ((error = ng_add_hook(node2, name2, &hook2))) {
114852419Sjulian		ng_destroy_hook(hook);
114952419Sjulian		return (error);
115052419Sjulian	}
115152419Sjulian	return (ng_connect(hook, hook2));
115252419Sjulian}
115370700Sjulian/************************************************************************
115470700Sjulian		Utility routines to send self messages
115570700Sjulian************************************************************************/
115670700Sjulian/*
115770700Sjulian * Static version of shutdown message. we don't want to need resources
115870700Sjulian * to shut down (we may be doing it to release resources because we ran out.
115970700Sjulian */
116070700Sjulianstatic struct	ng_mesg  ng_msg_shutdown = {
116170700Sjulian	{NG_VERSION,		/* u_char */
116270700Sjulian	0,			/* u_char spare */
116370700Sjulian	0,			/* u_int16_t arglen */
116470700Sjulian	NGF_STATIC,		/* u_int32_t flags */
116570700Sjulian	0,			/* u_int32_t token */
116670700Sjulian	NGM_GENERIC_COOKIE,	/* u_int32_t */
116770700Sjulian	NGM_SHUTDOWN,		/* u_int32_t */
116870700Sjulian	"shutdown"}		/* u_char[16] */
116970700Sjulian};
117070700Sjulian
117170700Sjulianint
117270700Sjulianng_rmnode_self(node_p here)
117370700Sjulian{
117470700Sjulian	item_p	item;
117570700Sjulian	struct	ng_mesg	*msg;
117652419Sjulian
117770700Sjulian	/*
117870700Sjulian	 * Use the static version to avoid needing
117970700Sjulian	 * memory allocation to succeed.
118070700Sjulian	 * The message is never written to and always the same.
118170700Sjulian	 */
118270700Sjulian	msg = &ng_msg_shutdown;
118370700Sjulian
118470700Sjulian	/*
118570700Sjulian	 * Try get a queue item to send it with.
118670700Sjulian	 * Hopefully since it has a reserve, we can get one.
118770700Sjulian	 * If we can't we are screwed anyhow.
118870700Sjulian	 * Increase the chances by flushing our queue first.
118970700Sjulian	 * We may free an item, (if we were the hog).
119070700Sjulian	 * Work in progress is allowed to complete.
119170700Sjulian	 * We also pretty much ensure that we come straight
119270700Sjulian	 * back in to do the shutdown. It may be a good idea
119370700Sjulian	 * to hold a reference actually to stop it from all
119470700Sjulian	 * going up in smoke.
119570700Sjulian	 */
119670784Sjulian/*	ng_flush_input_queue(&here->nd_input_queue); will mask problem  */
119770700Sjulian	item = ng_package_msg_self(here, NULL, msg);
119870700Sjulian	if (item == NULL) { /* it would have freed the msg except static */
119970700Sjulian		/* try again after flushing our queue */
120070784Sjulian		ng_flush_input_queue(&here->nd_input_queue);
120170700Sjulian		item = ng_package_msg_self(here, NULL, msg);
120270700Sjulian		if (item == NULL) {
120370700Sjulian			printf("failed to free node 0x%x\n", ng_node2ID(here));
120470700Sjulian			return (ENOMEM);
120570700Sjulian		}
120670700Sjulian	}
120770700Sjulian	return (ng_snd_item(item, 0));
120870700Sjulian}
120970700Sjulian
121070700Sjulian/***********************************************************************
121152419Sjulian * Parse and verify a string of the form:  <NODE:><PATH>
121252419Sjulian *
121352419Sjulian * Such a string can refer to a specific node or a specific hook
121452419Sjulian * on a specific node, depending on how you look at it. In the
121552419Sjulian * latter case, the PATH component must not end in a dot.
121652419Sjulian *
121752419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string
121852419Sjulian * of hook names separated by dots. This breaks out the original
121952419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp
122052419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to
122152419Sjulian * the final hook component of <PATH>, if any, otherwise NULL.
122252419Sjulian *
122352419Sjulian * This returns -1 if the path is malformed. The char ** are optional.
122470700Sjulian ***********************************************************************/
122552419Sjulianint
122652419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
122752419Sjulian{
122852419Sjulian	char   *node, *path, *hook;
122952419Sjulian	int     k;
123052419Sjulian
123152419Sjulian	/*
123252419Sjulian	 * Extract absolute NODE, if any
123352419Sjulian	 */
123452419Sjulian	for (path = addr; *path && *path != ':'; path++);
123552419Sjulian	if (*path) {
123652419Sjulian		node = addr;	/* Here's the NODE */
123752419Sjulian		*path++ = '\0';	/* Here's the PATH */
123852419Sjulian
123952419Sjulian		/* Node name must not be empty */
124052419Sjulian		if (!*node)
124152419Sjulian			return -1;
124252419Sjulian
124352419Sjulian		/* A name of "." is OK; otherwise '.' not allowed */
124452419Sjulian		if (strcmp(node, ".") != 0) {
124552419Sjulian			for (k = 0; node[k]; k++)
124652419Sjulian				if (node[k] == '.')
124752419Sjulian					return -1;
124852419Sjulian		}
124952419Sjulian	} else {
125052419Sjulian		node = NULL;	/* No absolute NODE */
125152419Sjulian		path = addr;	/* Here's the PATH */
125252419Sjulian	}
125352419Sjulian
125452419Sjulian	/* Snoop for illegal characters in PATH */
125552419Sjulian	for (k = 0; path[k]; k++)
125652419Sjulian		if (path[k] == ':')
125752419Sjulian			return -1;
125852419Sjulian
125952419Sjulian	/* Check for no repeated dots in PATH */
126052419Sjulian	for (k = 0; path[k]; k++)
126152419Sjulian		if (path[k] == '.' && path[k + 1] == '.')
126252419Sjulian			return -1;
126352419Sjulian
126452419Sjulian	/* Remove extra (degenerate) dots from beginning or end of PATH */
126552419Sjulian	if (path[0] == '.')
126652419Sjulian		path++;
126752419Sjulian	if (*path && path[strlen(path) - 1] == '.')
126852419Sjulian		path[strlen(path) - 1] = 0;
126952419Sjulian
127052419Sjulian	/* If PATH has a dot, then we're not talking about a hook */
127152419Sjulian	if (*path) {
127252419Sjulian		for (hook = path, k = 0; path[k]; k++)
127352419Sjulian			if (path[k] == '.') {
127452419Sjulian				hook = NULL;
127552419Sjulian				break;
127652419Sjulian			}
127752419Sjulian	} else
127852419Sjulian		path = hook = NULL;
127952419Sjulian
128052419Sjulian	/* Done */
128152419Sjulian	if (nodep)
128252419Sjulian		*nodep = node;
128352419Sjulian	if (pathp)
128452419Sjulian		*pathp = path;
128552419Sjulian	if (hookp)
128652419Sjulian		*hookp = hook;
128752419Sjulian	return (0);
128852419Sjulian}
128952419Sjulian
129052419Sjulian/*
129152419Sjulian * Given a path, which may be absolute or relative, and a starting node,
129270700Sjulian * return the destination node.
129352419Sjulian */
129452419Sjulianint
129570700Sjulianng_path2noderef(node_p here, const char *address,
129670700Sjulian				node_p *destp, hook_p *lasthook)
129752419Sjulian{
129852419Sjulian	char    fullpath[NG_PATHLEN + 1];
129952419Sjulian	char   *nodename, *path, pbuf[2];
130070700Sjulian	node_p  node, oldnode;
130152419Sjulian	char   *cp;
130259728Sjulian	hook_p hook = NULL;
130352419Sjulian
130452419Sjulian	/* Initialize */
130570784Sjulian	if (destp == NULL) {
130670784Sjulian		TRAP_ERROR
130752419Sjulian		return EINVAL;
130870784Sjulian	}
130952419Sjulian	*destp = NULL;
131052419Sjulian
131152419Sjulian	/* Make a writable copy of address for ng_path_parse() */
131252419Sjulian	strncpy(fullpath, address, sizeof(fullpath) - 1);
131352419Sjulian	fullpath[sizeof(fullpath) - 1] = '\0';
131452419Sjulian
131552419Sjulian	/* Parse out node and sequence of hooks */
131652419Sjulian	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
131752419Sjulian		TRAP_ERROR;
131852419Sjulian		return EINVAL;
131952419Sjulian	}
132052419Sjulian	if (path == NULL) {
132152419Sjulian		pbuf[0] = '.';	/* Needs to be writable */
132252419Sjulian		pbuf[1] = '\0';
132352419Sjulian		path = pbuf;
132452419Sjulian	}
132552419Sjulian
132670700Sjulian	/*
132770700Sjulian	 * For an absolute address, jump to the starting node.
132870700Sjulian	 * Note that this holds a reference on the node for us.
132970700Sjulian	 * Don't forget to drop the reference if we don't need it.
133070700Sjulian	 */
133152419Sjulian	if (nodename) {
133270700Sjulian		node = ng_name2noderef(here, nodename);
133352419Sjulian		if (node == NULL) {
133470784Sjulian			TRAP_ERROR
133552419Sjulian			return (ENOENT);
133652419Sjulian		}
133770700Sjulian	} else {
133870700Sjulian		if (here == NULL) {
133970700Sjulian			TRAP_ERROR
134070700Sjulian			return (EINVAL);
134170700Sjulian		}
134252419Sjulian		node = here;
134370784Sjulian		NG_NODE_REF(node);
134470700Sjulian	}
134552419Sjulian
134670700Sjulian	/*
134770700Sjulian	 * Now follow the sequence of hooks
134870700Sjulian	 * XXX
134970700Sjulian	 * We actually cannot guarantee that the sequence
135070700Sjulian	 * is not being demolished as we crawl along it
135170700Sjulian	 * without extra-ordinary locking etc.
135270700Sjulian	 * So this is a bit dodgy to say the least.
135370700Sjulian	 * We can probably hold up some things by holding
135470700Sjulian	 * the nodelist mutex for the time of this
135570700Sjulian	 * crawl if we wanted.. At least that way we wouldn't have to
135670700Sjulian	 * worry about the nodes dissappearing, but the hooks would still
135770700Sjulian	 * be a problem.
135870700Sjulian	 */
135952419Sjulian	for (cp = path; node != NULL && *cp != '\0'; ) {
136052419Sjulian		char *segment;
136152419Sjulian
136252419Sjulian		/*
136352419Sjulian		 * Break out the next path segment. Replace the dot we just
136452419Sjulian		 * found with a NUL; "cp" points to the next segment (or the
136552419Sjulian		 * NUL at the end).
136652419Sjulian		 */
136752419Sjulian		for (segment = cp; *cp != '\0'; cp++) {
136852419Sjulian			if (*cp == '.') {
136952419Sjulian				*cp++ = '\0';
137052419Sjulian				break;
137152419Sjulian			}
137252419Sjulian		}
137352419Sjulian
137452419Sjulian		/* Empty segment */
137552419Sjulian		if (*segment == '\0')
137652419Sjulian			continue;
137752419Sjulian
137852419Sjulian		/* We have a segment, so look for a hook by that name */
137954096Sarchie		hook = ng_findhook(node, segment);
138052419Sjulian
138152419Sjulian		/* Can't get there from here... */
138252419Sjulian		if (hook == NULL
138370784Sjulian		    || NG_HOOK_PEER(hook) == NULL
138470784Sjulian		    || NG_HOOK_NOT_VALID(hook)
138570784Sjulian		    || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) {
138652419Sjulian			TRAP_ERROR;
138770784Sjulian			NG_NODE_UNREF(node);
138870784Sjulian#if 0
138970784Sjulian			printf("hooknotvalid %s %s %d %d %d %d ",
139070784Sjulian					path,
139170784Sjulian					segment,
139270784Sjulian					hook == NULL,
139370784Sjulian		     			NG_HOOK_PEER(hook) == NULL,
139470784Sjulian		     			NG_HOOK_NOT_VALID(hook),
139570784Sjulian		     			NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)));
139670784Sjulian#endif
139752419Sjulian			return (ENOENT);
139852419Sjulian		}
139952419Sjulian
140070700Sjulian		/*
140170700Sjulian		 * Hop on over to the next node
140270700Sjulian		 * XXX
140370700Sjulian		 * Big race conditions here as hooks and nodes go away
140470700Sjulian		 * *** Idea.. store an ng_ID_t in each hook and use that
140570700Sjulian		 * instead of the direct hook in this crawl?
140670700Sjulian		 */
140770700Sjulian		oldnode = node;
140870784Sjulian		if ((node = NG_PEER_NODE(hook)))
140970784Sjulian			NG_NODE_REF(node);	/* XXX RACE */
141070784Sjulian		NG_NODE_UNREF(oldnode);	/* XXX another race */
141170784Sjulian		if (NG_NODE_NOT_VALID(node)) {
141270784Sjulian			NG_NODE_UNREF(node);	/* XXX more races */
141370700Sjulian			node = NULL;
141470700Sjulian		}
141552419Sjulian	}
141652419Sjulian
141752419Sjulian	/* If node somehow missing, fail here (probably this is not needed) */
141852419Sjulian	if (node == NULL) {
141952419Sjulian		TRAP_ERROR;
142052419Sjulian		return (ENXIO);
142152419Sjulian	}
142252419Sjulian
142352419Sjulian	/* Done */
142452419Sjulian	*destp = node;
142559900Sarchie	if (lasthook != NULL)
142670784Sjulian		*lasthook = (hook ? NG_HOOK_PEER(hook) : NULL);
142752419Sjulian	return (0);
142852419Sjulian}
142952419Sjulian
143070700Sjulian/***************************************************************\
143170700Sjulian* Input queue handling.
143270700Sjulian* All activities are submitted to the node via the input queue
143370700Sjulian* which implements a multiple-reader/single-writer gate.
143470700Sjulian* Items which cannot be handled immeditly are queued.
143570700Sjulian*
143670700Sjulian* read-write queue locking inline functions			*
143770700Sjulian\***************************************************************/
143870700Sjulian
143970700Sjulianstatic __inline item_p ng_dequeue(struct ng_queue * ngq);
144070700Sjulianstatic __inline item_p ng_acquire_read(struct ng_queue * ngq,
144170700Sjulian					item_p  item);
144270700Sjulianstatic __inline item_p ng_acquire_write(struct ng_queue * ngq,
144370700Sjulian					item_p  item);
144470700Sjulianstatic __inline void	ng_leave_read(struct ng_queue * ngq);
144570700Sjulianstatic __inline void	ng_leave_write(struct ng_queue * ngq);
144670700Sjulianstatic __inline void	ng_queue_rw(struct ng_queue * ngq,
144770700Sjulian					item_p  item, int rw);
144870700Sjulian
144952419Sjulian/*
145070700Sjulian * Definition of the bits fields in the ng_queue flag word.
145170700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle
145270700Sjulian * with them.
145370700Sjulian *
145470700Sjulian * The ordering here is important! don't shuffle these. If you add
145570700Sjulian * READ_PENDING to the word when it has READ_PENDING already set, you
145670700Sjulian * generate a carry into the reader count, this you atomically add a reader,
145770700Sjulian * and remove the pending reader count! Similarly for the pending writer
145870700Sjulian * flag, adding WRITE_PENDING generates a carry and sets the WRITER_ACTIVE
145970700Sjulian * flag, while clearing WRITE_PENDING. When 'SINGLE_THREAD_ONLY' is set, then
146070700Sjulian * it is only permitted to do WRITER operations. Reader operations will
146170700Sjulian * result in errors.
146270700Sjulian * But that "hack" is unnecessary: "cpp" can do the math for us!
146352419Sjulian */
146470700Sjulian/*-
146570700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet)
146670700Sjulian                       |
146770700Sjulian                       V
146870700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
146970700Sjulian| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
147070700Sjulian|A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |R|A|W|S|
147170700Sjulian| | | | | | | | | | | | | | | | | | | | | | | | | | | | |P|W|P|T|
147270700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+
147370700Sjulian\_________________________ ____________________________/ | | | |
147470700Sjulian                          V                              | | | |
147570700Sjulian                [active reader count]                    | | | |
147670700Sjulian                                                         | | | |
147770700Sjulian        Read Pending ------------------------------------+ | | |
147870700Sjulian                                                           | | |
147970700Sjulian        Active Writer -------------------------------------+ | |
148070700Sjulian                                                             | |
148170700Sjulian        Write Pending ---------------------------------------+ |
148270700Sjulian                                                               |
148370700Sjulian        Single Threading Only ---------------------------------+
148470700Sjulian*/
148570700Sjulian#define	SINGLE_THREAD_ONLY 0x00000001	/* if set, even reads single thread */
148670700Sjulian#define WRITE_PENDING	0x00000002
148770700Sjulian#define	WRITER_ACTIVE	0x00000004
148870700Sjulian#define READ_PENDING	0x00000008
148970700Sjulian#define	READER_INCREMENT 0x00000010
149070700Sjulian#define	READER_MASK	0xfffffff0	/* Not valid if WRITER_ACTIVE is set */
149170700Sjulian#define SAFETY_BARRIER	0x00100000	/* 64K items queued should be enough */
149270700Sjulian/*
149370700Sjulian * Taking into account the current state of the queue and node, possibly take
149470700Sjulian * the next entry off the queue and return it. Return NULL if there was
149570700Sjulian * nothing we could return, either because there really was nothing there, or
149670700Sjulian * because the node was in a state where it cannot yet process the next item
149770700Sjulian * on the queue.
149870700Sjulian *
149970700Sjulian * This MUST MUST MUST be called with the mutex held.
150070700Sjulian */
150170700Sjulianstatic __inline item_p
150270700Sjulianng_dequeue(struct ng_queue *ngq)
150370700Sjulian{
150470700Sjulian	item_p item;
150570700Sjulian	u_int		add_arg;
150670700Sjulian	/*
150770700Sjulian	 * If there is a writer, then the answer is "no". Everything else
150870700Sjulian	 * stops when there is a WRITER.
150970700Sjulian	 */
151070700Sjulian	if (ngq->q_flags & WRITER_ACTIVE) {
151170700Sjulian		return (NULL);
151270700Sjulian	}
151370700Sjulian	/* Now take a look at what's on the queue and what's running */
151470700Sjulian	if ((ngq->q_flags & ~(READER_MASK | SINGLE_THREAD_ONLY)) == READ_PENDING) {
151570700Sjulian		/*
151670700Sjulian		 * It was a reader and we have no write active. We don't care
151770700Sjulian		 * how many readers are already active. Adjust the count for
151870700Sjulian		 * the item we are about to dequeue. Adding READ_PENDING to
151970700Sjulian		 * the exisiting READ_PENDING clears it and generates a carry
152070700Sjulian		 * into the reader count.
152170700Sjulian		 */
152270700Sjulian		add_arg = READ_PENDING;
152370700Sjulian	} else if ((ngq->q_flags & ~SINGLE_THREAD_ONLY) == WRITE_PENDING) {
152470700Sjulian		/*
152570700Sjulian		 * There is a pending write, no readers and no active writer.
152670700Sjulian		 * This means we can go ahead with the pending writer. Note
152770700Sjulian		 * the fact that we now have a writer, ready for when we take
152870700Sjulian		 * it off the queue.
152970700Sjulian		 *
153070700Sjulian		 * We don't need to worry about a possible collision with the
153170700Sjulian		 * fasttrack reader.
153270700Sjulian		 *
153370700Sjulian		 * The fasttrack thread may take a long time to discover that we
153470700Sjulian		 * are running so we would have an inconsistent state in the
153570700Sjulian		 * flags for a while. Since we ignore the reader count
153670700Sjulian		 * entirely when the WRITER_ACTIVE flag is set, this should
153770700Sjulian		 * not matter (in fact it is defined that way). If it tests
153870700Sjulian		 * the flag before this operation, the WRITE_PENDING flag
153970700Sjulian		 * will make it fail, and if it tests it later, the
154070700Sjulian		 * ACTIVE_WRITER flag will do the same. If it is SO slow that
154170700Sjulian		 * we have actually completed the operation, and neither flag
154270700Sjulian		 * is set (nor the READ_PENDING) by the time that it tests
154370700Sjulian		 * the flags, then it is actually ok for it to continue. If
154470700Sjulian		 * it completes and we've finished and the read pending is
154570700Sjulian		 * set it still fails.
154670700Sjulian		 *
154770700Sjulian		 * So we can just ignore it,  as long as we can ensure that the
154870700Sjulian		 * transition from WRITE_PENDING state to the WRITER_ACTIVE
154970700Sjulian		 * state is atomic.
155070700Sjulian		 *
155170700Sjulian		 * After failing, first it will be held back by the mutex, then
155270700Sjulian		 * when it can proceed, it will queue its request, then it
155370700Sjulian		 * would arrive at this function. Usually it will have to
155470700Sjulian		 * leave empty handed because the ACTIVE WRITER bit wil be
155570700Sjulian		 * set.
155670700Sjulian		 */
155770700Sjulian		/*
155870700Sjulian		 * Adjust the flags for the item we are about to dequeue.
155970700Sjulian		 * Adding WRITE_PENDING to the exisiting WRITE_PENDING clears
156070700Sjulian		 * it and generates a carry into the WRITER_ACTIVE flag, all
156170700Sjulian		 * atomically.
156270700Sjulian		 */
156370700Sjulian		add_arg = WRITE_PENDING;
156470700Sjulian		/*
156570700Sjulian		 * We want to write "active writer, no readers " Now go make
156670700Sjulian		 * it true. In fact there may be a number in the readers
156770700Sjulian		 * count but we know it is not true and will be fixed soon.
156870700Sjulian		 * We will fix the flags for the next pending entry in a
156970700Sjulian		 * moment.
157070700Sjulian		 */
157170700Sjulian	} else {
157270700Sjulian		/*
157370700Sjulian		 * We can't dequeue anything.. return and say so. Probably we
157470700Sjulian		 * have a write pending and the readers count is non zero. If
157570700Sjulian		 * we got here because a reader hit us just at the wrong
157670700Sjulian		 * moment with the fasttrack code, and put us in a strange
157770700Sjulian		 * state, then it will be through in just a moment, (as soon
157870700Sjulian		 * as we release the mutex) and keep things moving.
157970700Sjulian		 */
158070700Sjulian		return (0);
158170700Sjulian	}
158252419Sjulian
158370700Sjulian	/*
158470700Sjulian	 * Now we dequeue the request (whatever it may be) and correct the
158570700Sjulian	 * pending flags and the next and last pointers.
158670700Sjulian	 */
158770700Sjulian	item = ngq->queue;
158870700Sjulian	ngq->queue = item->el_next;
158970700Sjulian	if (ngq->last == &(item->el_next)) {
159070700Sjulian		/*
159170700Sjulian		 * that was the last entry in the queue so set the 'last
159270700Sjulian		 * pointer up correctly and make sure the pending flags are
159370700Sjulian		 * clear.
159470700Sjulian		 */
159570700Sjulian		ngq->last = &(ngq->queue);
159670700Sjulian		/*
159770700Sjulian		 * Whatever flag was set is cleared and the carry sets the
159870700Sjulian		 * correct new active state/count. So we don't need to change
159970700Sjulian		 * add_arg.
160070700Sjulian		 */
160170700Sjulian	} else {
160270700Sjulian		if ((ngq->queue->el_flags & NGQF_TYPE) == NGQF_READER) {
160370700Sjulian			/*
160470700Sjulian			 * If we had a READ_PENDING and have another one, we
160570700Sjulian			 * just want to add READ_PENDING twice (the same as
160670700Sjulian			 * adding READER_INCREMENT). If we had WRITE_PENDING,
160770700Sjulian			 * we want to add READ_PENDING + WRITE_PENDING to
160870700Sjulian			 * clear the old WRITE_PENDING, set ACTIVE_WRITER,
160970700Sjulian			 * and set READ_PENDING. Either way we just add
161070700Sjulian			 * READ_PENDING to whatever we already had.
161170700Sjulian			 */
161270700Sjulian			add_arg += READ_PENDING;
161370700Sjulian		} else {
161470700Sjulian			/*
161570700Sjulian			 * If we had a WRITE_PENDING and have another one, we
161670700Sjulian			 * just want to add WRITE_PENDING twice (the same as
161770700Sjulian			 * adding ACTIVE_WRITER). If we had READ_PENDING, we
161870700Sjulian			 * want to add READ_PENDING + WRITE_PENDING to clear
161970700Sjulian			 * the old READ_PENDING, increment the readers, and
162070700Sjulian			 * set WRITE_PENDING. Either way we just add
162170700Sjulian			 * WRITE_PENDING to whatever we already had.
162270700Sjulian			 */
162370700Sjulian			add_arg += WRITE_PENDING;
162470700Sjulian		}
162570700Sjulian	}
162670700Sjulian	atomic_add_long(&ngq->q_flags, add_arg);
162770700Sjulian	/*
162870700Sjulian	 * We have successfully cleared the old pending flag, set the new one
162970700Sjulian	 * if it is needed, and incremented the appropriate active field.
163070700Sjulian	 * (all in one atomic addition.. wow)
163170700Sjulian	 */
163270700Sjulian	return (item);
163370700Sjulian}
163452419Sjulian
163570700Sjulian/*
163670700Sjulian * Queue a packet to be picked up by someone else.
163770700Sjulian * We really don't care who, but we can't or don't want to hang around
163870700Sjulian * to process it ourselves. We are probably an interrupt routine..
163970700Sjulian * 1 = writer, 0 = reader
164070700Sjulian * We should set something to indicate NETISR requested
164170700Sjulian * If it's the first item queued.
164270700Sjulian */
164370700Sjulian#define NGQRW_R 0
164470700Sjulian#define NGQRW_W 1
164570700Sjulianstatic __inline void
164670700Sjulianng_queue_rw(struct ng_queue * ngq, item_p  item, int rw)
164770700Sjulian{
164870700Sjulian	item->el_next = NULL;	/* maybe not needed */
164970700Sjulian	*ngq->last = item;
165070700Sjulian	/*
165170700Sjulian	 * If it was the first item in the queue then we need to
165270700Sjulian	 * set the last pointer and the type flags.
165370700Sjulian	 */
165470700Sjulian	if (ngq->last == &(ngq->queue)) {
165570700Sjulian		/*
165670700Sjulian		 * When called with constants for rw, the optimiser will
165770700Sjulian		 * remove the unneeded branch below.
165870700Sjulian		 */
165970700Sjulian		if (rw == NGQRW_W) {
166070700Sjulian			atomic_add_long(&ngq->q_flags, WRITE_PENDING);
166170700Sjulian		} else {
166270700Sjulian			atomic_add_long(&ngq->q_flags, READ_PENDING);
166370700Sjulian		}
166470700Sjulian	}
166570700Sjulian	ngq->last = &(item->el_next);
166670700Sjulian}
166752419Sjulian
166870700Sjulian
166952419Sjulian/*
167070700Sjulian * This function 'cheats' in that it first tries to 'grab' the use of the
167170700Sjulian * node, without going through the mutex. We can do this becasue of the
167270700Sjulian * semantics of the lock. The semantics include a clause that says that the
167370700Sjulian * value of the readers count is invalid if the WRITER_ACTIVE flag is set. It
167470700Sjulian * also says that the WRITER_ACTIVE flag cannot be set if the readers count
167570700Sjulian * is not zero. Note that this talks about what is valid to SET the
167670700Sjulian * WRITER_ACTIVE flag, because from the moment it is set, the value if the
167770700Sjulian * reader count is immaterial, and not valid. The two 'pending' flags have a
167870700Sjulian * similar effect, in that If they are orthogonal to the two active fields in
167970700Sjulian * how they are set, but if either is set, the attempted 'grab' need to be
168070700Sjulian * backed out because there is earlier work, and we maintain ordering in the
168170700Sjulian * queue. The result of this is that the reader request can try obtain use of
168270700Sjulian * the node with only a single atomic addition, and without any of the mutex
168370700Sjulian * overhead. If this fails the operation degenerates to the same as for other
168470700Sjulian * cases.
168570700Sjulian *
168652419Sjulian */
168770700Sjulianstatic __inline item_p
168870700Sjulianng_acquire_read(struct ng_queue *ngq, item_p item)
168952419Sjulian{
169052419Sjulian
169170700Sjulian	/* ######### Hack alert ######### */
169270700Sjulian	atomic_add_long(&ngq->q_flags, READER_INCREMENT);
169370700Sjulian	if ((ngq->q_flags & (~READER_MASK)) == 0) {
169470700Sjulian		/* Successfully grabbed node */
169570700Sjulian		return (item);
169670700Sjulian	}
169770700Sjulian	/* undo the damage if we didn't succeed */
169870700Sjulian	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
169970700Sjulian
170070700Sjulian	/* ######### End Hack alert ######### */
170170700Sjulian	mtx_enter((&ngq->q_mtx), MTX_SPIN);
170269922Sjulian	/*
170370700Sjulian	 * Try again. Another processor (or interrupt for that matter) may
170470700Sjulian	 * have removed the last queued item that was stopping us from
170570700Sjulian	 * running, between the previous test, and the moment that we took
170670700Sjulian	 * the mutex. (Or maybe a writer completed.)
170769922Sjulian	 */
170870700Sjulian	if ((ngq->q_flags & (~READER_MASK)) == 0) {
170970700Sjulian		atomic_add_long(&ngq->q_flags, READER_INCREMENT);
171070700Sjulian		mtx_exit((&ngq->q_mtx), MTX_SPIN);
171170700Sjulian		return (item);
171270700Sjulian	}
171370700Sjulian
171470700Sjulian	/*
171570700Sjulian	 * Quick check that we are doing things right.
171670700Sjulian	 */
171770700Sjulian	if (ngq->q_flags & SINGLE_THREAD_ONLY) {
171870700Sjulian		panic("Calling single treaded queue incorrectly");
171970700Sjulian	}
172070700Sjulian
172170700Sjulian	/*
172270700Sjulian	 * and queue the request for later.
172370700Sjulian	 */
172470700Sjulian	item->el_flags |= NGQF_TYPE;
172570700Sjulian	ng_queue_rw(ngq, item, NGQRW_R);
172670700Sjulian
172770700Sjulian	/*
172870700Sjulian	 * Ok, so that's the item successfully queued for later. So now we
172970700Sjulian	 * see if we can dequeue something to run instead.
173070700Sjulian	 */
173170700Sjulian	item = ng_dequeue(ngq);
173270700Sjulian	mtx_exit(&(ngq->q_mtx), MTX_SPIN);
173370700Sjulian	return (item);
173470700Sjulian}
173570700Sjulian
173670700Sjulianstatic __inline item_p
173770700Sjulianng_acquire_write(struct ng_queue *ngq, item_p item)
173870700Sjulian{
173970700Sjulianrestart:
174070700Sjulian	mtx_enter(&(ngq->q_mtx), MTX_SPIN);
174170700Sjulian	/*
174270700Sjulian	 * If there are no readers, no writer, and no pending packets, then
174370700Sjulian	 * we can just go ahead. In all other situations we need to queue the
174470700Sjulian	 * request
174570700Sjulian	 */
174670700Sjulian	if ((ngq->q_flags & (~SINGLE_THREAD_ONLY)) == 0) {
174770700Sjulian		atomic_add_long(&ngq->q_flags, WRITER_ACTIVE);
174870700Sjulian		mtx_exit((&ngq->q_mtx), MTX_SPIN);
174970700Sjulian		if (ngq->q_flags & READER_MASK) {
175070700Sjulian			/* Collision with fast-track reader */
175170700Sjulian			atomic_add_long(&ngq->q_flags, -WRITER_ACTIVE);
175270700Sjulian			goto restart;
175369922Sjulian		}
175470700Sjulian
175570700Sjulian		return (item);
175652419Sjulian	}
175752419Sjulian
175870700Sjulian	/*
175970700Sjulian	 * and queue the request for later.
176070700Sjulian	 */
176170700Sjulian	item->el_flags &= ~NGQF_TYPE;
176270700Sjulian	ng_queue_rw(ngq, item, NGQRW_W);
176370700Sjulian
176470700Sjulian	/*
176570700Sjulian	 * Ok, so that's the item successfully queued for later. So now we
176670700Sjulian	 * see if we can dequeue something to run instead.
176770700Sjulian	 */
176870700Sjulian	item = ng_dequeue(ngq);
176970700Sjulian	mtx_exit(&(ngq->q_mtx), MTX_SPIN);
177070700Sjulian	return (item);
177170700Sjulian}
177270700Sjulian
177370700Sjulianstatic __inline void
177470700Sjulianng_leave_read(struct ng_queue *ngq)
177570700Sjulian{
177670700Sjulian	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
177770700Sjulian}
177870700Sjulian
177970700Sjulianstatic __inline void
178070700Sjulianng_leave_write(struct ng_queue *ngq)
178170700Sjulian{
178270700Sjulian	atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE);
178370700Sjulian}
178470700Sjulian
178570700Sjulianstatic void
178670700Sjulianng_flush_input_queue(struct ng_queue * ngq)
178770700Sjulian{
178870700Sjulian	item_p item;
178970700Sjulian	u_int		add_arg;
179070700Sjulian	mtx_enter(&ngq->q_mtx, MTX_SPIN);
179170700Sjulian	for (;;) {
179270700Sjulian		/* Now take a look at what's on the queue */
179370700Sjulian		if (ngq->q_flags & READ_PENDING) {
179470700Sjulian			add_arg = -READ_PENDING;
179570700Sjulian		} else if (ngq->q_flags & WRITE_PENDING) {
179670700Sjulian			add_arg = -WRITE_PENDING;
179770700Sjulian		} else {
179870700Sjulian			break;
179970700Sjulian		}
180070700Sjulian
180170700Sjulian		item = ngq->queue;
180270700Sjulian		ngq->queue = item->el_next;
180370700Sjulian		if (ngq->last == &(item->el_next)) {
180470700Sjulian			ngq->last = &(ngq->queue);
180570700Sjulian		} else {
180670700Sjulian			if ((ngq->queue->el_flags & NGQF_TYPE) == NGQF_READER) {
180770700Sjulian				add_arg += READ_PENDING;
180870700Sjulian			} else {
180970700Sjulian				add_arg += WRITE_PENDING;
181070700Sjulian			}
181170700Sjulian		}
181270700Sjulian		atomic_add_long(&ngq->q_flags, add_arg);
181370700Sjulian
181470700Sjulian		mtx_exit(&ngq->q_mtx, MTX_SPIN);
181570700Sjulian		NG_FREE_ITEM(item);
181670700Sjulian		mtx_enter(&ngq->q_mtx, MTX_SPIN);
181770700Sjulian	}
181870700Sjulian	mtx_exit(&ngq->q_mtx, MTX_SPIN);
181970700Sjulian}
182070700Sjulian
182170700Sjulian/***********************************************************************
182270700Sjulian* Externally visible method for sending or queueing messages or data.
182370700Sjulian***********************************************************************/
182470700Sjulian
182570700Sjulian/*
182670700Sjulian * MACRO WILL DO THE JOB OF CALLING ng_package_msg IN CALLER
182770700Sjulian * before we are called. The user code should have filled out the item
182870700Sjulian * correctly by this stage:
182970700Sjulian * Common:
183070700Sjulian *    reference to destination node.
183170700Sjulian *    Reference to destination rcv hook if relevant.
183270700Sjulian * Data:
183370700Sjulian *    pointer to mbuf
183470700Sjulian *    pointer to metadata
183570700Sjulian * Control_Message:
183670700Sjulian *    pointer to msg.
183770700Sjulian *    ID of original sender node. (return address)
183870700Sjulian *
183970700Sjulian * The nodes have several routines and macros to help with this task:
184070700Sjulian * ng_package_msg()
184170700Sjulian * ng_package_data() do much of the work.
184270700Sjulian * ng_retarget_msg
184370700Sjulian * ng_retarget_data
184470700Sjulian */
184570700Sjulian
184670700Sjulianint
184770700Sjulianng_snd_item(item_p item, int queue)
184870700Sjulian{
184970700Sjulian	hook_p hook = item->el_hook;
185070700Sjulian	node_p dest = item->el_dest;
185170700Sjulian	int rw;
185270700Sjulian	int error = 0, ierror;
185370700Sjulian	item_p	oitem;
185470784Sjulian	struct ng_queue * ngq = &dest->nd_input_queue;
185570700Sjulian
185670784Sjulian#ifdef	NETGRAPH_DEBUG
185770700Sjulian        _ngi_check(item, __FILE__, __LINE__);
185870700Sjulian#endif
185970700Sjulian
186070700Sjulian	if (item == NULL) {
186170784Sjulian		TRAP_ERROR;
186270700Sjulian		return (EINVAL);	/* failed to get queue element */
186370700Sjulian	}
186470700Sjulian	if (dest == NULL) {
186570700Sjulian		NG_FREE_ITEM(item);
186670784Sjulian		TRAP_ERROR;
186770700Sjulian		return (EINVAL);	/* No address */
186870700Sjulian	}
186970700Sjulian	if ((item->el_flags & NGQF_D_M) == NGQF_DATA) {
187069922Sjulian		/*
187170700Sjulian		 * DATA MESSAGE
187270700Sjulian		 * Delivered to a node via a non-optional hook.
187370700Sjulian		 * Both should be present in the item even though
187470700Sjulian		 * the node is derivable from the hook.
187570700Sjulian		 * References are held on both by the item.
187669922Sjulian		 */
187770784Sjulian#ifdef	NETGRAPH_DEBUG
187870700Sjulian        _ngi_check(item, __FILE__, __LINE__);
187970700Sjulian#endif
188070700Sjulian		CHECK_DATA_MBUF(NGI_M(item));
188170700Sjulian		if (hook == NULL) {
188270700Sjulian			NG_FREE_ITEM(item);
188370784Sjulian			TRAP_ERROR;
188470700Sjulian			return(EINVAL);
188570700Sjulian		}
188670784Sjulian		if ((NG_HOOK_NOT_VALID(hook))
188770784Sjulian		|| (NG_NODE_NOT_VALID(NG_HOOK_NODE(hook)))) {
188870700Sjulian			NG_FREE_ITEM(item);
188970700Sjulian			return (ENOTCONN);
189069922Sjulian		}
189170784Sjulian		if ((hook->hk_flags & HK_QUEUE)) {
189270700Sjulian			queue = 1;
189370700Sjulian		}
189470700Sjulian		/* By default data is a reader in the locking scheme */
189570700Sjulian		item->el_flags |= NGQF_READER;
189670700Sjulian		rw = NGQRW_R;
189770700Sjulian	} else {
189870700Sjulian		/*
189970700Sjulian		 * CONTROL MESSAGE
190070700Sjulian		 * Delivered to a node.
190170700Sjulian		 * Hook is optional.
190270700Sjulian		 * References are held by the item on the node and
190370700Sjulian		 * the hook if it is present.
190470700Sjulian		 */
190570784Sjulian		if (hook && (hook->hk_flags & HK_QUEUE)) {
190670700Sjulian			queue = 1;
190770700Sjulian		}
190870700Sjulian		/* Data messages count as writers unles explicitly exempted */
190970700Sjulian		if (NGI_MSG(item)->header.cmd & NGM_READONLY) {
191070700Sjulian			item->el_flags |= NGQF_READER;
191170700Sjulian			rw = NGQRW_R;
191270700Sjulian		} else {
191370700Sjulian			item->el_flags &= ~NGQF_TYPE;
191470700Sjulian			rw = NGQRW_W;
191570700Sjulian		}
191669922Sjulian	}
191770700Sjulian	/*
191870700Sjulian	 * If the node specifies single threading, force writer semantics
191970700Sjulian	 * Similarly the node may say one hook always produces writers.
192070700Sjulian	 * These are over-rides.
192170700Sjulian	 */
192270700Sjulian	if ((ngq->q_flags & SINGLE_THREAD_ONLY)
192370784Sjulian	|| (dest->nd_flags & NG_FORCE_WRITER)
192470784Sjulian	|| (hook && (hook->hk_flags & HK_FORCE_WRITER))) {
192570700Sjulian			rw = NGQRW_W;
192670700Sjulian			item->el_flags &= ~NGQF_TYPE;
192770700Sjulian	}
192870700Sjulian	if (queue) {
192970700Sjulian		/* Put it on the queue for that node*/
193070784Sjulian#ifdef	NETGRAPH_DEBUG
193170700Sjulian        _ngi_check(item, __FILE__, __LINE__);
193270700Sjulian#endif
193370700Sjulian		mtx_enter(&(ngq->q_mtx), MTX_SPIN);
193470700Sjulian		ng_queue_rw(ngq, item, rw);
193570700Sjulian		mtx_exit(&(ngq->q_mtx), MTX_SPIN);
193670700Sjulian		/*
193770700Sjulian		 * If there are active elements then we can rely on
193870700Sjulian		 * them. if not we should not rely on another packet
193970700Sjulian		 * coming here by another path,
194070700Sjulian		 * so it is best to put us in the netisr list.
194170700Sjulian		 */
194270700Sjulian		if ((ngq->q_flags & (READER_MASK|WRITER_ACTIVE)) == 0) {
194370700Sjulian			ng_setisr(ngq->q_node);
194470700Sjulian		}
194570700Sjulian		return (0);
194670700Sjulian	}
194770700Sjulian	/*
194870700Sjulian	 * Take a queue item and a node and see if we can apply the item to
194970700Sjulian	 * the node. We may end up getting a different item to apply instead.
195070700Sjulian	 * Will allow for a piggyback reply only in the case where
195170700Sjulian	 * there is no queueing.
195270700Sjulian	 */
195369922Sjulian
195470700Sjulian	oitem = item;
195570700Sjulian	/*
195670700Sjulian	 * We already decided how we will be queueud or treated.
195770700Sjulian	 * Try get the appropriate operating permission.
195870700Sjulian	 */
195970700Sjulian 	if (rw == NGQRW_R) {
196070700Sjulian		item = ng_acquire_read(ngq, item);
196170700Sjulian	} else {
196270700Sjulian		item = ng_acquire_write(ngq, item);
196370700Sjulian	}
196452419Sjulian
196570700Sjulian	/*
196670700Sjulian	 * May have come back with a different item.
196770700Sjulian	 * or maybe none at all. The one we started with will
196870700Sjulian	 * have been queued in thises cases.
196970700Sjulian	 */
197070700Sjulian	if (item == NULL) {
197170700Sjulian		return (0);
197270700Sjulian	}
197352419Sjulian
197470784Sjulian#ifdef	NETGRAPH_DEBUG
197570700Sjulian        _ngi_check(item, __FILE__, __LINE__);
197670700Sjulian#endif
197770700Sjulian	ierror = ng_apply_item(dest, item); /* drops r/w lock when done */
197852419Sjulian
197970700Sjulian	/* only return an error if it was our initial item.. (compat hack) */
198070700Sjulian	if (oitem == item) {
198170700Sjulian		error = ierror;
198270700Sjulian	}
198370700Sjulian
198452419Sjulian	/*
198570700Sjulian	 * Now we've handled the packet we brought, (or a friend of it) let's
198670700Sjulian	 * look for any other packets that may have been queued up. We hold
198770700Sjulian	 * no locks, so if someone puts something in the queue after
198870700Sjulian	 * we check that it is empty, it is their problem
198970700Sjulian	 * to ensure it is processed. If we have the netisr thread cme in here
199070700Sjulian	 * while we still say we have stuff to do, we may get a boost
199170700Sjulian	 * in SMP systems. :-)
199252419Sjulian	 */
199370700Sjulian	for (;;) {
199470700Sjulian		/* quick hack to save all that mutex stuff */
199570700Sjulian		if ((ngq->q_flags & (WRITE_PENDING | READ_PENDING)) == 0) {
199670784Sjulian			if (dest->nd_flags & NG_WORKQ)
199770700Sjulian				ng_worklist_remove(dest);
199870700Sjulian			return (0);
199970700Sjulian		}
200070700Sjulian		/*
200170700Sjulian		 * dequeue acquires and adjusts the input_queue as it dequeues
200270700Sjulian		 * packets. It acquires the rw lock as needed.
200370700Sjulian		 */
200470700Sjulian		mtx_enter(&ngq->q_mtx, MTX_SPIN);
200570700Sjulian		item = ng_dequeue(ngq);
200670700Sjulian		mtx_exit(&ngq->q_mtx, MTX_SPIN);
200770700Sjulian		if (!item) {
200870700Sjulian			/*
200970700Sjulian			 * If we have no work to do
201070700Sjulian			 * then we certainly don't need to be
201170700Sjulian			 * on the worklist.
201270700Sjulian			 */
201370784Sjulian			if (dest->nd_flags & NG_WORKQ)
201470700Sjulian				ng_worklist_remove(dest);
201570700Sjulian			return (0);
201670700Sjulian		}
201770784Sjulian#ifdef	NETGRAPH_DEBUG
201870700Sjulian        _ngi_check(item, __FILE__, __LINE__);
201970700Sjulian#endif
202070700Sjulian
202170700Sjulian		/*
202270700Sjulian		 * We have the appropriate lock, so run the item.
202370700Sjulian		 * When finished it will drop the lock accordingly
202470700Sjulian		 */
202570700Sjulian
202670700Sjulian		ierror = ng_apply_item(dest, item);
202770700Sjulian		/*
202870700Sjulian		 * only return an error if it was our initial
202970700Sjulian		 * item.. (compat hack)
203070700Sjulian		 */
203170700Sjulian		if (oitem == item) {
203270700Sjulian			error = ierror;
203370700Sjulian		}
203470700Sjulian	}
203570700Sjulian	return (0);
203652419Sjulian}
203752419Sjulian
203852419Sjulian/*
203970700Sjulian * We have an item that was possibly queued somewhere.
204070700Sjulian * It should contain all the information needed
204170700Sjulian * to run it on the appropriate node/hook.
204252419Sjulian */
204352419Sjulianstatic int
204470700Sjulianng_apply_item(node_p node, item_p item)
204552419Sjulian{
204670700Sjulian	hook_p  hook;
204770700Sjulian	int was_reader = ((item->el_flags & NGQF_TYPE));
204852419Sjulian	int error = 0;
204970700Sjulian	ng_rcvdata_t *rcvdata;
205052419Sjulian
205170700Sjulian	hook = item->el_hook;
205270784Sjulian	item->el_hook = NULL;	/* so NG_FREE_ITEM doesn't NG_HOOK_UNREF() */
205370700Sjulian	/* We already have the node.. assume responsibility */
205470700Sjulian	/* And the reference */
205570700Sjulian	/* node = item->el_dest; */
205670700Sjulian	item->el_dest = NULL;	/* same as for the hook above */
205770784Sjulian#ifdef	NETGRAPH_DEBUG
205870700Sjulian        _ngi_check(item, __FILE__, __LINE__);
205970700Sjulian#endif
206070700Sjulian
206170700Sjulian	switch (item->el_flags & NGQF_D_M) {
206270700Sjulian	case NGQF_DATA:
206370700Sjulian		/*
206470700Sjulian		 * Check things are still ok as when we were queued.
206570700Sjulian		 */
206670700Sjulian
206770700Sjulian		if ((hook == NULL)
206870784Sjulian		|| NG_HOOK_NOT_VALID(hook)
206970784Sjulian		|| NG_NODE_NOT_VALID(NG_HOOK_NODE(hook))
207070784Sjulian		|| ((rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata) == NULL)) {
207170700Sjulian			error = EIO;
207270700Sjulian			NG_FREE_ITEM(item);
207370700Sjulian		} else {
207470700Sjulian			error = (*rcvdata)(hook, item);
207570700Sjulian		}
207670700Sjulian		break;
207770700Sjulian	case NGQF_MESG:
207870700Sjulian
207970700Sjulian		if (hook) {
208070700Sjulian			item->el_hook = NULL;
208170784Sjulian			if (NG_HOOK_NOT_VALID(hook)) {
208270700Sjulian			/*
208370700Sjulian			 * If the hook has been zapped then we can't use it.
208470700Sjulian			 * Immediatly drop its reference.
208570700Sjulian			 * The message may not need it.
208670700Sjulian			 */
208770784Sjulian				NG_HOOK_UNREF(hook);
208870700Sjulian				hook = NULL;
208970700Sjulian			}
209070700Sjulian		}
209170700Sjulian		/*
209270700Sjulian		 * Similarly, if the node is a zombie there is
209370700Sjulian		 * nothing we can do with it, drop everything.
209470700Sjulian		 */
209570784Sjulian		if (NG_NODE_NOT_VALID(node)) {
209670784Sjulian			TRAP_ERROR;
209770700Sjulian			error = EINVAL;
209870700Sjulian			NG_FREE_ITEM(item);
209970700Sjulian		} else {
210070700Sjulian			/*
210170700Sjulian			 * Call the appropriate message handler for the object.
210270700Sjulian			 * It is up to the message handler to free the message.
210370700Sjulian			 * If it's a generic message, handle it generically,
210470700Sjulian			 * otherwise call the type's message handler
210570700Sjulian			 * (if it exists)
210670700Sjulian			 * XXX (race). Remember that a queued message may
210770700Sjulian			 * reference a node or hook that has just been
210870700Sjulian			 * invalidated. It will exist as the queue code
210970700Sjulian			 * is holding a reference, but..
211070700Sjulian			 */
211170700Sjulian
211270700Sjulian			struct ng_mesg *msg = NGI_MSG(item);
211370700Sjulian
211470700Sjulian			if ((msg->header.typecookie == NGM_GENERIC_COOKIE)
211570700Sjulian			&& ((msg->header.flags & NGF_RESP) == 0)) {
211670700Sjulian				error = ng_generic_msg(node, item, hook);
211770700Sjulian			} else {
211870784Sjulian				if ((node)->nd_type->rcvmsg != NULL) {
211970784Sjulian					error = (*(node)->nd_type->rcvmsg)((node),
212070700Sjulian						(item), (hook));
212170700Sjulian				} else {
212270700Sjulian					TRAP_ERROR;
212370700Sjulian					error = EINVAL; /* XXX */
212470700Sjulian					NG_FREE_ITEM(item);
212570700Sjulian				}
212670700Sjulian			}
212770700Sjulian			/* item is now invalid */
212870700Sjulian		}
212970700Sjulian		break;
213070700Sjulian	}
213170700Sjulian	/*
213270700Sjulian	 * We held references on some of the resources
213370700Sjulian	 * that we took from the item. Now that we have
213470700Sjulian	 * finished doing everything, drop those references.
213570700Sjulian	 */
213670700Sjulian	if (hook) {
213770784Sjulian		NG_HOOK_UNREF(hook);
213870700Sjulian	}
213970700Sjulian
214070700Sjulian	if (was_reader) {
214170784Sjulian		ng_leave_read(&node->nd_input_queue);
214270700Sjulian	} else {
214370784Sjulian		ng_leave_write(&node->nd_input_queue);
214470700Sjulian	}
214570784Sjulian	NG_NODE_UNREF(node);
214670700Sjulian	return (error);
214770700Sjulian}
214870700Sjulian
214970700Sjulian/***********************************************************************
215070700Sjulian * Implement the 'generic' control messages
215170700Sjulian ***********************************************************************/
215270700Sjulianstatic int
215370700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook)
215470700Sjulian{
215570700Sjulian	int error = 0;
215670700Sjulian	struct ng_mesg *msg;
215770700Sjulian	struct ng_mesg *resp = NULL;
215870700Sjulian
215970700Sjulian	NGI_GET_MSG(item, msg);
216052419Sjulian	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
216170784Sjulian		TRAP_ERROR
216270700Sjulian		error = EINVAL;
216370700Sjulian		goto out;
216452419Sjulian	}
216552419Sjulian	switch (msg->header.cmd) {
216652419Sjulian	case NGM_SHUTDOWN:
216752419Sjulian		ng_rmnode(here);
216852419Sjulian		break;
216952419Sjulian	case NGM_MKPEER:
217052419Sjulian	    {
217152419Sjulian		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
217252419Sjulian
217352419Sjulian		if (msg->header.arglen != sizeof(*mkp)) {
217470784Sjulian			TRAP_ERROR
217570700Sjulian			error = EINVAL;
217670700Sjulian			break;
217752419Sjulian		}
217852419Sjulian		mkp->type[sizeof(mkp->type) - 1] = '\0';
217952419Sjulian		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
218052419Sjulian		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
218152419Sjulian		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
218252419Sjulian		break;
218352419Sjulian	    }
218452419Sjulian	case NGM_CONNECT:
218552419Sjulian	    {
218652419Sjulian		struct ngm_connect *const con =
218752419Sjulian			(struct ngm_connect *) msg->data;
218852419Sjulian		node_p node2;
218952419Sjulian
219052419Sjulian		if (msg->header.arglen != sizeof(*con)) {
219170784Sjulian			TRAP_ERROR
219270700Sjulian			error = EINVAL;
219370700Sjulian			break;
219452419Sjulian		}
219552419Sjulian		con->path[sizeof(con->path) - 1] = '\0';
219652419Sjulian		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
219752419Sjulian		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
219870700Sjulian		/* Don't forget we get a reference.. */
219970700Sjulian		error = ng_path2noderef(here, con->path, &node2, NULL);
220052419Sjulian		if (error)
220152419Sjulian			break;
220252419Sjulian		error = ng_con_nodes(here, con->ourhook, node2, con->peerhook);
220370784Sjulian		NG_NODE_UNREF(node2);
220452419Sjulian		break;
220552419Sjulian	    }
220652419Sjulian	case NGM_NAME:
220752419Sjulian	    {
220852419Sjulian		struct ngm_name *const nam = (struct ngm_name *) msg->data;
220952419Sjulian
221052419Sjulian		if (msg->header.arglen != sizeof(*nam)) {
221170784Sjulian			TRAP_ERROR
221270700Sjulian			error = EINVAL;
221370700Sjulian			break;
221452419Sjulian		}
221552419Sjulian		nam->name[sizeof(nam->name) - 1] = '\0';
221652419Sjulian		error = ng_name_node(here, nam->name);
221752419Sjulian		break;
221852419Sjulian	    }
221952419Sjulian	case NGM_RMHOOK:
222052419Sjulian	    {
222152419Sjulian		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
222252419Sjulian		hook_p hook;
222352419Sjulian
222452419Sjulian		if (msg->header.arglen != sizeof(*rmh)) {
222570784Sjulian			TRAP_ERROR
222670700Sjulian			error = EINVAL;
222770700Sjulian			break;
222852419Sjulian		}
222952419Sjulian		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
223054096Sarchie		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
223152419Sjulian			ng_destroy_hook(hook);
223252419Sjulian		break;
223352419Sjulian	    }
223452419Sjulian	case NGM_NODEINFO:
223552419Sjulian	    {
223652419Sjulian		struct nodeinfo *ni;
223752419Sjulian
223870700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
223952419Sjulian		if (resp == NULL) {
224052419Sjulian			error = ENOMEM;
224152419Sjulian			break;
224252419Sjulian		}
224352419Sjulian
224452419Sjulian		/* Fill in node info */
224570700Sjulian		ni = (struct nodeinfo *) resp->data;
224670784Sjulian		if (NG_NODE_HAS_NAME(here))
224770784Sjulian			strncpy(ni->name, NG_NODE_NAME(here), NG_NODELEN);
224870784Sjulian		strncpy(ni->type, here->nd_type->name, NG_TYPELEN);
224952722Sjulian		ni->id = ng_node2ID(here);
225070784Sjulian		ni->hooks = here->nd_numhooks;
225152419Sjulian		break;
225252419Sjulian	    }
225352419Sjulian	case NGM_LISTHOOKS:
225452419Sjulian	    {
225570784Sjulian		const int nhooks = here->nd_numhooks;
225652419Sjulian		struct hooklist *hl;
225752419Sjulian		struct nodeinfo *ni;
225852419Sjulian		hook_p hook;
225952419Sjulian
226052419Sjulian		/* Get response struct */
226170700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*hl)
226270700Sjulian		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
226352419Sjulian		if (resp == NULL) {
226452419Sjulian			error = ENOMEM;
226552419Sjulian			break;
226652419Sjulian		}
226770700Sjulian		hl = (struct hooklist *) resp->data;
226852419Sjulian		ni = &hl->nodeinfo;
226952419Sjulian
227052419Sjulian		/* Fill in node info */
227170784Sjulian		if (NG_NODE_HAS_NAME(here))
227270784Sjulian			strncpy(ni->name, NG_NODE_NAME(here), NG_NODELEN);
227370784Sjulian		strncpy(ni->type, here->nd_type->name, NG_TYPELEN);
227452722Sjulian		ni->id = ng_node2ID(here);
227552419Sjulian
227652419Sjulian		/* Cycle through the linked list of hooks */
227752419Sjulian		ni->hooks = 0;
227870784Sjulian		LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) {
227952419Sjulian			struct linkinfo *const link = &hl->link[ni->hooks];
228052419Sjulian
228152419Sjulian			if (ni->hooks >= nhooks) {
228252419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
228352419Sjulian				    __FUNCTION__, "hooks");
228452419Sjulian				break;
228552419Sjulian			}
228670784Sjulian			if (NG_HOOK_NOT_VALID(hook))
228752419Sjulian				continue;
228870784Sjulian			strncpy(link->ourhook, NG_HOOK_NAME(hook), NG_HOOKLEN);
228970784Sjulian			strncpy(link->peerhook,
229070784Sjulian				NG_PEER_HOOK_NAME(hook), NG_HOOKLEN);
229170784Sjulian			if (NG_PEER_NODE_NAME(hook)[0] != '\0')
229252419Sjulian				strncpy(link->nodeinfo.name,
229370784Sjulian				    NG_PEER_NODE_NAME(hook), NG_NODELEN);
229452419Sjulian			strncpy(link->nodeinfo.type,
229570784Sjulian			   NG_PEER_NODE(hook)->nd_type->name, NG_TYPELEN);
229670784Sjulian			link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook));
229770784Sjulian			link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks;
229852419Sjulian			ni->hooks++;
229952419Sjulian		}
230052419Sjulian		break;
230152419Sjulian	    }
230252419Sjulian
230352419Sjulian	case NGM_LISTNAMES:
230452419Sjulian	case NGM_LISTNODES:
230552419Sjulian	    {
230652419Sjulian		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
230752419Sjulian		struct namelist *nl;
230852419Sjulian		node_p node;
230952419Sjulian		int num = 0;
231052419Sjulian
231170700Sjulian		mtx_enter(&ng_nodelist_mtx, MTX_DEF);
231252419Sjulian		/* Count number of nodes */
231370784Sjulian		LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
231470912Sjulian			if (NG_NODE_IS_VALID(node)
231570912Sjulian			&& (unnamed || NG_NODE_HAS_NAME(node))) {
231652419Sjulian				num++;
231770912Sjulian			}
231852419Sjulian		}
231970700Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
232052419Sjulian
232152419Sjulian		/* Get response struct */
232270700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*nl)
232370700Sjulian		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
232452419Sjulian		if (resp == NULL) {
232552419Sjulian			error = ENOMEM;
232652419Sjulian			break;
232752419Sjulian		}
232870700Sjulian		nl = (struct namelist *) resp->data;
232952419Sjulian
233052419Sjulian		/* Cycle through the linked list of nodes */
233152419Sjulian		nl->numnames = 0;
233270700Sjulian		mtx_enter(&ng_nodelist_mtx, MTX_DEF);
233370784Sjulian		LIST_FOREACH(node, &ng_nodelist, nd_nodes) {
233452419Sjulian			struct nodeinfo *const np = &nl->nodeinfo[nl->numnames];
233552419Sjulian
233652419Sjulian			if (nl->numnames >= num) {
233752419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
233852419Sjulian				    __FUNCTION__, "nodes");
233952419Sjulian				break;
234052419Sjulian			}
234170784Sjulian			if (NG_NODE_NOT_VALID(node))
234252419Sjulian				continue;
234370784Sjulian			if (!unnamed && (! NG_NODE_HAS_NAME(node)))
234452419Sjulian				continue;
234570784Sjulian			if (NG_NODE_HAS_NAME(node))
234670784Sjulian				strncpy(np->name, NG_NODE_NAME(node), NG_NODELEN);
234770784Sjulian			strncpy(np->type, node->nd_type->name, NG_TYPELEN);
234852722Sjulian			np->id = ng_node2ID(node);
234970784Sjulian			np->hooks = node->nd_numhooks;
235052419Sjulian			nl->numnames++;
235152419Sjulian		}
235270700Sjulian		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
235352419Sjulian		break;
235452419Sjulian	    }
235552419Sjulian
235652419Sjulian	case NGM_LISTTYPES:
235752419Sjulian	    {
235852419Sjulian		struct typelist *tl;
235952419Sjulian		struct ng_type *type;
236052419Sjulian		int num = 0;
236152419Sjulian
236270700Sjulian		mtx_enter(&ng_typelist_mtx, MTX_DEF);
236352419Sjulian		/* Count number of types */
236470912Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
236552419Sjulian			num++;
236670912Sjulian		}
236770700Sjulian		mtx_exit(&ng_typelist_mtx, MTX_DEF);
236852419Sjulian
236952419Sjulian		/* Get response struct */
237070700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*tl)
237170700Sjulian		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
237252419Sjulian		if (resp == NULL) {
237352419Sjulian			error = ENOMEM;
237452419Sjulian			break;
237552419Sjulian		}
237670700Sjulian		tl = (struct typelist *) resp->data;
237752419Sjulian
237852419Sjulian		/* Cycle through the linked list of types */
237952419Sjulian		tl->numtypes = 0;
238070700Sjulian		mtx_enter(&ng_typelist_mtx, MTX_DEF);
238170700Sjulian		LIST_FOREACH(type, &ng_typelist, types) {
238252419Sjulian			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
238352419Sjulian
238452419Sjulian			if (tl->numtypes >= num) {
238552419Sjulian				log(LOG_ERR, "%s: number of %s changed\n",
238652419Sjulian				    __FUNCTION__, "types");
238752419Sjulian				break;
238852419Sjulian			}
238959879Sarchie			strncpy(tp->type_name, type->name, NG_TYPELEN);
239052419Sjulian			tp->numnodes = type->refs;
239152419Sjulian			tl->numtypes++;
239252419Sjulian		}
239370700Sjulian		mtx_exit(&ng_typelist_mtx, MTX_DEF);
239452419Sjulian		break;
239552419Sjulian	    }
239652419Sjulian
239753913Sarchie	case NGM_BINARY2ASCII:
239853913Sarchie	    {
239964510Sarchie		int bufSize = 20 * 1024;	/* XXX hard coded constant */
240053913Sarchie		const struct ng_parse_type *argstype;
240153913Sarchie		const struct ng_cmdlist *c;
240270700Sjulian		struct ng_mesg *binary, *ascii;
240353913Sarchie
240453913Sarchie		/* Data area must contain a valid netgraph message */
240553913Sarchie		binary = (struct ng_mesg *)msg->data;
240653913Sarchie		if (msg->header.arglen < sizeof(struct ng_mesg)
240770912Sjulian		    || (msg->header.arglen - sizeof(struct ng_mesg)
240870912Sjulian		      < binary->header.arglen)) {
240970784Sjulian			TRAP_ERROR
241053913Sarchie			error = EINVAL;
241153913Sarchie			break;
241253913Sarchie		}
241353913Sarchie
241470700Sjulian		/* Get a response message with lots of room */
241570700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
241670159Sjulian		if (resp == NULL) {
241753913Sarchie			error = ENOMEM;
241853913Sarchie			break;
241953913Sarchie		}
242070700Sjulian		ascii = (struct ng_mesg *)resp->data;
242153913Sarchie
242253913Sarchie		/* Copy binary message header to response message payload */
242353913Sarchie		bcopy(binary, ascii, sizeof(*binary));
242453913Sarchie
242553913Sarchie		/* Find command by matching typecookie and command number */
242670784Sjulian		for (c = here->nd_type->cmdlist;
242753913Sarchie		    c != NULL && c->name != NULL; c++) {
242853913Sarchie			if (binary->header.typecookie == c->cookie
242953913Sarchie			    && binary->header.cmd == c->cmd)
243053913Sarchie				break;
243153913Sarchie		}
243253913Sarchie		if (c == NULL || c->name == NULL) {
243353913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
243453913Sarchie				if (binary->header.typecookie == c->cookie
243553913Sarchie				    && binary->header.cmd == c->cmd)
243653913Sarchie					break;
243753913Sarchie			}
243853913Sarchie			if (c->name == NULL) {
243970700Sjulian				NG_FREE_MSG(resp);
244053913Sarchie				error = ENOSYS;
244153913Sarchie				break;
244253913Sarchie			}
244353913Sarchie		}
244453913Sarchie
244553913Sarchie		/* Convert command name to ASCII */
244653913Sarchie		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
244753913Sarchie		    "%s", c->name);
244853913Sarchie
244953913Sarchie		/* Convert command arguments to ASCII */
245053913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
245153913Sarchie		    c->respType : c->mesgType;
245270912Sjulian		if (argstype == NULL) {
245353913Sarchie			*ascii->data = '\0';
245470912Sjulian		} else {
245553913Sarchie			if ((error = ng_unparse(argstype,
245653913Sarchie			    (u_char *)binary->data,
245753913Sarchie			    ascii->data, bufSize)) != 0) {
245870700Sjulian				NG_FREE_MSG(resp);
245953913Sarchie				break;
246053913Sarchie			}
246153913Sarchie		}
246253913Sarchie
246353913Sarchie		/* Return the result as struct ng_mesg plus ASCII string */
246453913Sarchie		bufSize = strlen(ascii->data) + 1;
246553913Sarchie		ascii->header.arglen = bufSize;
246670700Sjulian		resp->header.arglen = sizeof(*ascii) + bufSize;
246753913Sarchie		break;
246853913Sarchie	    }
246953913Sarchie
247053913Sarchie	case NGM_ASCII2BINARY:
247153913Sarchie	    {
247253913Sarchie		int bufSize = 2000;	/* XXX hard coded constant */
247353913Sarchie		const struct ng_cmdlist *c;
247453913Sarchie		const struct ng_parse_type *argstype;
247570700Sjulian		struct ng_mesg *ascii, *binary;
247659178Sarchie		int off = 0;
247753913Sarchie
247853913Sarchie		/* Data area must contain at least a struct ng_mesg + '\0' */
247953913Sarchie		ascii = (struct ng_mesg *)msg->data;
248070912Sjulian		if ((msg->header.arglen < sizeof(*ascii) + 1)
248170912Sjulian		    || (ascii->header.arglen < 1)
248270912Sjulian		    || (msg->header.arglen
248370912Sjulian		      < sizeof(*ascii) + ascii->header.arglen)) {
248470784Sjulian			TRAP_ERROR
248553913Sarchie			error = EINVAL;
248653913Sarchie			break;
248753913Sarchie		}
248853913Sarchie		ascii->data[ascii->header.arglen - 1] = '\0';
248953913Sarchie
249070700Sjulian		/* Get a response message with lots of room */
249170700Sjulian		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
249270159Sjulian		if (resp == NULL) {
249353913Sarchie			error = ENOMEM;
249453913Sarchie			break;
249553913Sarchie		}
249670700Sjulian		binary = (struct ng_mesg *)resp->data;
249753913Sarchie
249853913Sarchie		/* Copy ASCII message header to response message payload */
249953913Sarchie		bcopy(ascii, binary, sizeof(*ascii));
250053913Sarchie
250153913Sarchie		/* Find command by matching ASCII command string */
250270784Sjulian		for (c = here->nd_type->cmdlist;
250353913Sarchie		    c != NULL && c->name != NULL; c++) {
250453913Sarchie			if (strcmp(ascii->header.cmdstr, c->name) == 0)
250553913Sarchie				break;
250653913Sarchie		}
250753913Sarchie		if (c == NULL || c->name == NULL) {
250853913Sarchie			for (c = ng_generic_cmds; c->name != NULL; c++) {
250953913Sarchie				if (strcmp(ascii->header.cmdstr, c->name) == 0)
251053913Sarchie					break;
251153913Sarchie			}
251253913Sarchie			if (c->name == NULL) {
251370700Sjulian				NG_FREE_MSG(resp);
251453913Sarchie				error = ENOSYS;
251553913Sarchie				break;
251653913Sarchie			}
251753913Sarchie		}
251853913Sarchie
251953913Sarchie		/* Convert command name to binary */
252053913Sarchie		binary->header.cmd = c->cmd;
252153913Sarchie		binary->header.typecookie = c->cookie;
252253913Sarchie
252353913Sarchie		/* Convert command arguments to binary */
252453913Sarchie		argstype = (binary->header.flags & NGF_RESP) ?
252553913Sarchie		    c->respType : c->mesgType;
252670912Sjulian		if (argstype == NULL) {
252753913Sarchie			bufSize = 0;
252870912Sjulian		} else {
252953913Sarchie			if ((error = ng_parse(argstype, ascii->data,
253053913Sarchie			    &off, (u_char *)binary->data, &bufSize)) != 0) {
253170700Sjulian				NG_FREE_MSG(resp);
253253913Sarchie				break;
253353913Sarchie			}
253453913Sarchie		}
253553913Sarchie
253653913Sarchie		/* Return the result */
253753913Sarchie		binary->header.arglen = bufSize;
253870700Sjulian		resp->header.arglen = sizeof(*binary) + bufSize;
253953913Sarchie		break;
254053913Sarchie	    }
254153913Sarchie
254262471Sphk	case NGM_TEXT_CONFIG:
254352419Sjulian	case NGM_TEXT_STATUS:
254452419Sjulian		/*
254552419Sjulian		 * This one is tricky as it passes the command down to the
254652419Sjulian		 * actual node, even though it is a generic type command.
254770700Sjulian		 * This means we must assume that the item/msg is already freed
254852419Sjulian		 * when control passes back to us.
254952419Sjulian		 */
255070784Sjulian		if (here->nd_type->rcvmsg != NULL) {
255170700Sjulian			NGI_MSG(item) = msg; /* put it back as we found it */
255270784Sjulian			return((*here->nd_type->rcvmsg)(here, item, lasthook));
255352419Sjulian		}
255452419Sjulian		/* Fall through if rcvmsg not supported */
255552419Sjulian	default:
255652419Sjulian		TRAP_ERROR;
255752419Sjulian		error = EINVAL;
255852419Sjulian	}
255970700Sjulian	/*
256070700Sjulian	 * Sometimes a generic message may be statically allocated
256170700Sjulian	 * to avoid problems with allocating when in tight memeory situations.
256270700Sjulian	 * Don't free it if it is so.
256370700Sjulian	 * I break them appart here, because erros may cause a free if the item
256470700Sjulian	 * in which case we'd be doing it twice.
256570700Sjulian	 * they are kept together above, to simplify freeing.
256670700Sjulian	 */
256770700Sjulianout:
256870700Sjulian	NG_RESPOND_MSG(error, here, item, resp);
256970700Sjulian	if ( msg && ((msg->header.flags & NGF_STATIC) == 0))
257070700Sjulian		NG_FREE_MSG(msg);
257152419Sjulian	return (error);
257252419Sjulian}
257352419Sjulian
257452419Sjulian/*
257559879Sarchie * Copy a 'meta'.
257659879Sarchie *
257759879Sarchie * Returns new meta, or NULL if original meta is NULL or ENOMEM.
257859879Sarchie */
257959879Sarchiemeta_p
258059879Sarchieng_copy_meta(meta_p meta)
258159879Sarchie{
258259879Sarchie	meta_p meta2;
258359879Sarchie
258459879Sarchie	if (meta == NULL)
258559879Sarchie		return (NULL);
258670700Sjulian	MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH_META, M_NOWAIT);
258759879Sarchie	if (meta2 == NULL)
258859879Sarchie		return (NULL);
258959879Sarchie	meta2->allocated_len = meta->used_len;
259059879Sarchie	bcopy(meta, meta2, meta->used_len);
259159879Sarchie	return (meta2);
259259879Sarchie}
259359879Sarchie
259452419Sjulian/************************************************************************
259552419Sjulian			Module routines
259652419Sjulian************************************************************************/
259752419Sjulian
259852419Sjulian/*
259952419Sjulian * Handle the loading/unloading of a netgraph node type module
260052419Sjulian */
260152419Sjulianint
260252419Sjulianng_mod_event(module_t mod, int event, void *data)
260352419Sjulian{
260452419Sjulian	struct ng_type *const type = data;
260552419Sjulian	int s, error = 0;
260652419Sjulian
260752419Sjulian	switch (event) {
260852419Sjulian	case MOD_LOAD:
260952419Sjulian
261052419Sjulian		/* Register new netgraph node type */
261152419Sjulian		s = splnet();
261252419Sjulian		if ((error = ng_newtype(type)) != 0) {
261352419Sjulian			splx(s);
261452419Sjulian			break;
261552419Sjulian		}
261652419Sjulian
261752419Sjulian		/* Call type specific code */
261852419Sjulian		if (type->mod_event != NULL)
261970700Sjulian			if ((error = (*type->mod_event)(mod, event, data))) {
262070700Sjulian				mtx_enter(&ng_typelist_mtx, MTX_DEF);
262152419Sjulian				LIST_REMOVE(type, types);
262270700Sjulian				mtx_exit(&ng_typelist_mtx, MTX_DEF);
262370700Sjulian			}
262452419Sjulian		splx(s);
262552419Sjulian		break;
262652419Sjulian
262752419Sjulian	case MOD_UNLOAD:
262852419Sjulian		s = splnet();
262952419Sjulian		if (type->refs != 0)		/* make sure no nodes exist! */
263052419Sjulian			error = EBUSY;
263152419Sjulian		else {
263252419Sjulian			if (type->mod_event != NULL) {	/* check with type */
263352419Sjulian				error = (*type->mod_event)(mod, event, data);
263452419Sjulian				if (error != 0) {	/* type refuses.. */
263552419Sjulian					splx(s);
263652419Sjulian					break;
263752419Sjulian				}
263852419Sjulian			}
263970700Sjulian			mtx_enter(&ng_typelist_mtx, MTX_DEF);
264052419Sjulian			LIST_REMOVE(type, types);
264170700Sjulian			mtx_exit(&ng_typelist_mtx, MTX_DEF);
264252419Sjulian		}
264352419Sjulian		splx(s);
264452419Sjulian		break;
264552419Sjulian
264652419Sjulian	default:
264752419Sjulian		if (type->mod_event != NULL)
264852419Sjulian			error = (*type->mod_event)(mod, event, data);
264952419Sjulian		else
265052419Sjulian			error = 0;		/* XXX ? */
265152419Sjulian		break;
265252419Sjulian	}
265352419Sjulian	return (error);
265452419Sjulian}
265552419Sjulian
265652419Sjulian/*
265752419Sjulian * Handle loading and unloading for this code.
265852419Sjulian * The only thing we need to link into is the NETISR strucure.
265952419Sjulian */
266052419Sjulianstatic int
266152419Sjulianngb_mod_event(module_t mod, int event, void *data)
266252419Sjulian{
266352419Sjulian	int s, error = 0;
266452419Sjulian
266552419Sjulian	switch (event) {
266652419Sjulian	case MOD_LOAD:
266752419Sjulian		/* Register line discipline */
266870700Sjulian		mtx_init(&ng_worklist_mtx, "netgraph worklist mutex", 0);
266970700Sjulian		mtx_init(&ng_typelist_mtx, "netgraph types mutex", 0);
267070700Sjulian		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", 0);
267170700Sjulian		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", 0);
267270700Sjulian		mtx_init(&ngq_mtx, "netgraph netisr mutex", 0);
267352419Sjulian		s = splimp();
267452419Sjulian		error = register_netisr(NETISR_NETGRAPH, ngintr);
267552419Sjulian		splx(s);
267652419Sjulian		break;
267752419Sjulian	case MOD_UNLOAD:
267852419Sjulian		/* You cant unload it because an interface may be using it.  */
267952419Sjulian		error = EBUSY;
268052419Sjulian		break;
268152419Sjulian	default:
268252419Sjulian		error = EOPNOTSUPP;
268352419Sjulian		break;
268452419Sjulian	}
268552419Sjulian	return (error);
268652419Sjulian}
268752419Sjulian
268852419Sjulianstatic moduledata_t netgraph_mod = {
268952419Sjulian	"netgraph",
269052419Sjulian	ngb_mod_event,
269152419Sjulian	(NULL)
269252419Sjulian};
269352419SjulianDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
269452419Sjulian
269552419Sjulian/************************************************************************
269670700Sjulian			Queue element get/free routines
269752419Sjulian************************************************************************/
269852419Sjulian
269952419Sjulian
270070700Sjulianstatic int			allocated;	/* number of items malloc'd */
270170700Sjulianstatic int			maxalloc = 128;	/* limit the damage of a leak */
270270700Sjulianstatic const int		ngqfreemax = 64;/* cache at most this many */
270370700Sjulianstatic const int		ngqfreelow = 4; /* try malloc if free < this */
270470700Sjulianstatic volatile int		ngqfreesize;	/* number of cached entries */
270570784Sjulian#ifdef	NETGRAPH_DEBUG
270670700Sjulianstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
270770700Sjulian#endif
270852419Sjulian/*
270952419Sjulian * Get a queue entry
271070700Sjulian * This is usually called when a packet first enters netgraph.
271170700Sjulian * By definition, this is usually from an interrupt, or from a user.
271270700Sjulian * Users are not so important, but try be quick for the times that it's
271370700Sjulian * an interrupt. Use atomic operations to cope with collisions
271470700Sjulian * with interrupts and other processors. Assumes MALLOC is SMP safe.
271570700Sjulian * XXX If reserve is low, we should try to get 2 from malloc as this
271670700Sjulian * would indicate it often fails.
271752419Sjulian */
271870700Sjulianstatic item_p
271952419Sjulianng_getqblk(void)
272052419Sjulian{
272170700Sjulian	item_p item = NULL;
272252419Sjulian
272370700Sjulian	/*
272470700Sjulian	 * Try get a cached queue block, or else allocate a new one
272570700Sjulian	 * If we are less than our reserve, try malloc. If malloc
272670700Sjulian	 * fails, then that's what the reserve is for...
272770700Sjulian	 * Don't completely trust ngqfreesize, as it is subject
272870700Sjulian	 * to races.. (it'll eventually catch up but may be out by one or two
272970700Sjulian	 * for brief moments(under SMP or interrupts).
273070700Sjulian	 * ngqfree is the final arbiter. We have our little reserve
273170700Sjulian	 * because we use M_NOWAIT for malloc. This just helps us
273270700Sjulian	 * avoid dropping packets while not increasing the time
273370700Sjulian	 * we take to service the interrupt (on average) (we hope).
273470700Sjulian	 */
273570700Sjulian	for (;;) {
273670700Sjulian		if ((ngqfreesize < ngqfreelow) || (ngqfree == NULL)) {
273770700Sjulian			if (allocated < maxalloc) {  /* don't leak forever */
273870700Sjulian				MALLOC(item, item_p ,
273970700Sjulian				    sizeof(*item), M_NETGRAPH_ITEM,
274070700Sjulian				    (M_NOWAIT | M_ZERO));
274170700Sjulian				if (item) {
274270784Sjulian#ifdef	NETGRAPH_DEBUG
274370700Sjulian					TAILQ_INSERT_TAIL(&ng_itemlist,
274470700Sjulian								item, all);
274570784Sjulian#endif	/* NETGRAPH_DEBUG */
274670700Sjulian					atomic_add_int(&allocated, 1);
274770700Sjulian					break;
274870700Sjulian				}
274970700Sjulian			}
275070700Sjulian		}
275152419Sjulian
275270700Sjulian		/*
275370700Sjulian		 * We didn't or couldn't malloc.
275470700Sjulian		 * try get one from our cache.
275570700Sjulian		 * item must be NULL to get here.
275670700Sjulian		 */
275770700Sjulian		if ((item = ngqfree) != NULL) {
275870700Sjulian			/*
275970700Sjulian			 * Atomically try grab the first item
276070700Sjulian			 * and put it's successor in its place.
276170700Sjulian			 * If we fail, just try again.. someone else
276270700Sjulian			 * beat us to this one or freed one.
276370700Sjulian			 * Don't worry about races with ngqfreesize.
276470700Sjulian			 * Close enough is good enough..
276570700Sjulian			 */
276670700Sjulian			if (atomic_cmpset_ptr(&ngqfree, item, item->el_next)) {
276770700Sjulian				atomic_subtract_int(&ngqfreesize, 1);
276870700Sjulian				break;
276970700Sjulian			}
277070700Sjulian			item = NULL;
277170700Sjulian		} else {
277270700Sjulian			/* We really ran out */
277370700Sjulian			break;
277452419Sjulian		}
277552419Sjulian	}
277670700Sjulian	item->el_flags &= ~NGQF_FREE;
277770700Sjulian	return (item);
277852419Sjulian}
277952419Sjulian
278052419Sjulian/*
278152419Sjulian * Release a queue entry
278252419Sjulian */
278370700Sjulianvoid
278470700Sjulianng_free_item(item_p item)
278552419Sjulian{
278652419Sjulian
278770700Sjulian	/*
278870700Sjulian	 * The item may hold resources on it's own. We need to free
278970700Sjulian	 * these before we can free the item. What they are depends upon
279070700Sjulian	 * what kind of item it is. it is important that nodes zero
279170700Sjulian	 * out pointers to resources that they remove from the item
279270700Sjulian	 * or we release them again here.
279370700Sjulian	 */
279470700Sjulian	if (item->el_flags & NGQF_FREE) {
279570700Sjulian		panic(" Freeing free queue item");
279652419Sjulian	}
279770700Sjulian	switch (item->el_flags & NGQF_D_M) {
279870700Sjulian	case NGQF_DATA:
279970700Sjulian		/* If we have an mbuf and metadata still attached.. */
280070700Sjulian		NG_FREE_M(_NGI_M(item));
280170700Sjulian		NG_FREE_META(_NGI_META(item));
280270700Sjulian		break;
280370700Sjulian	case NGQF_MESG:
280470700Sjulian		_NGI_RETADDR(item) = NULL;
280570700Sjulian		NG_FREE_MSG(_NGI_MSG(item));
280670700Sjulian		break;
280752419Sjulian	}
280870700Sjulian		/* If we still have a node or hook referenced... */
280970700Sjulian	if (item->el_dest) {
281070784Sjulian		NG_NODE_UNREF(item->el_dest);
281170700Sjulian		item->el_dest = NULL;
281270700Sjulian	}
281370700Sjulian	if (item->el_hook) {
281470784Sjulian		NG_HOOK_UNREF(item->el_hook);
281570700Sjulian		item->el_hook = NULL;
281670700Sjulian	}
281770700Sjulian	item->el_flags |= NGQF_FREE;
281852419Sjulian
281970700Sjulian	/*
282070700Sjulian	 * We have freed any resources held by the item.
282170700Sjulian	 * now we can free the item itself.
282270700Sjulian	 */
282370700Sjulian	if (ngqfreesize < ngqfreemax) { /* don't worry about races */
282470700Sjulian		for (;;) {
282570700Sjulian			item->el_next = ngqfree;
282670700Sjulian			if (atomic_cmpset_ptr(&ngqfree, item->el_next, item)) {
282770700Sjulian				break;
282870700Sjulian			}
282970700Sjulian		}
283070700Sjulian		atomic_add_int(&ngqfreesize, 1);
283170700Sjulian	} else {
283270700Sjulian		/* This is the only place that should use this Macro */
283370784Sjulian#ifdef	NETGRAPH_DEBUG
283470700Sjulian		TAILQ_REMOVE(&ng_itemlist, item, all);
283570784Sjulian#endif	/* NETGRAPH_DEBUG */
283670700Sjulian		NG_FREE_ITEM_REAL(item);
283770700Sjulian		atomic_subtract_int(&allocated, 1);
283870700Sjulian	}
283970700Sjulian}
284052419Sjulian
284170784Sjulian#ifdef	NETGRAPH_DEBUG
284270700Sjulianvoid
284370784Sjuliandumphook (hook_p hook, char *file, int line)
284470784Sjulian{
284570784Sjulian	printf("hook: name %s, %d refs, Last touched:\n",
284670784Sjulian		_NG_HOOK_NAME(hook), hook->hk_refs);
284770784Sjulian	printf("	Last active @ %s, line %d\n",
284870784Sjulian		hook->lastfile, hook->lastline);
284970784Sjulian	if (line) {
285070784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
285170784Sjulian	}
285270784Sjulian}
285370784Sjulian
285470784Sjulianvoid
285570784Sjuliandumpnode(node_p node, char *file, int line)
285670784Sjulian{
285770784Sjulian	printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n",
285870784Sjulian		ng_node2ID(node), node->nd_type->name,
285970784Sjulian		node->nd_numhooks, node->nd_flags,
286070784Sjulian		node->nd_refs, node->nd_name);
286170784Sjulian	printf("	Last active @ %s, line %d\n",
286270784Sjulian		node->lastfile, node->lastline);
286370784Sjulian	if (line) {
286470784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
286570784Sjulian	}
286670784Sjulian}
286770784Sjulian
286870784Sjulianvoid
286970700Sjuliandumpitem(item_p item, char *file, int line)
287070700Sjulian{
287170700Sjulian	if (item->el_flags & NGQF_FREE) {
287270700Sjulian		printf(" Free item, freed at %s, line %d\n",
287370700Sjulian			item->lastfile, item->lastline);
287452419Sjulian	} else {
287570700Sjulian		printf(" ACTIVE item, last used at %s, line %d",
287670700Sjulian			item->lastfile, item->lastline);
287770700Sjulian		if ((item->el_flags & NGQF_D_M) == NGQF_MESG) {
287870700Sjulian			printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
287970700Sjulian		} else {
288070700Sjulian			printf(" - [data]\n");
288170700Sjulian		}
288252419Sjulian	}
288370784Sjulian	if (line) {
288470784Sjulian		printf(" problem discovered at file %s, line %d\n", file, line);
288570784Sjulian		if (item->el_dest) {
288670784Sjulian			printf("node %p ([%x])\n",
288770784Sjulian				item->el_dest, ng_node2ID(item->el_dest));
288870784Sjulian		}
288970784Sjulian	}
289070700Sjulian}
289152419Sjulian
289270784Sjulianstatic void
289370784Sjulianng_dumpitems(void)
289470784Sjulian{
289570784Sjulian	item_p item;
289670784Sjulian	int i = 1;
289770784Sjulian	TAILQ_FOREACH(item, &ng_itemlist, all) {
289870784Sjulian		printf("[%d] ", i++);
289970784Sjulian		dumpitem(item, NULL, 0);
290070784Sjulian	}
290170784Sjulian}
290270784Sjulian
290370784Sjulianstatic void
290470784Sjulianng_dumpnodes(void)
290570784Sjulian{
290670784Sjulian	node_p node;
290770784Sjulian	int i = 1;
290870784Sjulian	SLIST_FOREACH(node, &ng_allnodes, nd_all) {
290970784Sjulian		printf("[%d] ", i++);
291070784Sjulian		dumpnode(node, NULL, 0);
291170784Sjulian	}
291270784Sjulian}
291370784Sjulian
291470784Sjulianstatic void
291570784Sjulianng_dumphooks(void)
291670784Sjulian{
291770784Sjulian	hook_p hook;
291870784Sjulian	int i = 1;
291970784Sjulian	SLIST_FOREACH(hook, &ng_allhooks, hk_all) {
292070784Sjulian		printf("[%d] ", i++);
292170784Sjulian		dumphook(hook, NULL, 0);
292270784Sjulian	}
292370784Sjulian}
292470784Sjulian
292570700Sjulianstatic int
292670700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
292770700Sjulian{
292870784Sjulian	static int count;
292970700Sjulian	int error;
293070700Sjulian	int val;
293170700Sjulian	int i;
293270700Sjulian
293370700Sjulian	val = allocated;
293470700Sjulian	i = 1;
293570700Sjulian	error = sysctl_handle_int(oidp, &val, sizeof(int), req);
293670784Sjulian	if(count++  & 1) { /* for some reason sysctl calls it twice */
293770784Sjulian		ng_dumpitems();
293870784Sjulian		ng_dumpnodes();
293970784Sjulian		ng_dumphooks();
294070700Sjulian	}
294170700Sjulian	return error;
294252419Sjulian}
294352419Sjulian
294470700SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RD,
294570700Sjulian    0, 0, sysctl_debug_ng_dump_items, "I", "Number of allocated items");
294670784Sjulian#endif	/* NETGRAPH_DEBUG */
294770700Sjulian
294870700Sjulian
294970700Sjulian/***********************************************************************
295070700Sjulian* Worklist routines
295170700Sjulian**********************************************************************/
295270700Sjulian/* NETISR thread enters here */
295352419Sjulian/*
295470700Sjulian * Pick a node off the list of nodes with work,
295570700Sjulian * try get an item to process off it.
295670700Sjulian * If there are no more, remove the node from the list.
295752419Sjulian */
295870700Sjulianstatic void
295970700Sjulianngintr(void)
296052419Sjulian{
296170700Sjulian	item_p item;
296270700Sjulian	node_p  node = NULL;
296352419Sjulian
296470700Sjulian	for (;;) {
296570700Sjulian		mtx_enter(&ng_worklist_mtx, MTX_SPIN);
296670700Sjulian		node = TAILQ_FIRST(&ng_worklist);
296770700Sjulian		if (!node) {
296870700Sjulian			mtx_exit(&ng_worklist_mtx, MTX_SPIN);
296970700Sjulian			break;
297069922Sjulian		}
297170784Sjulian		TAILQ_REMOVE(&ng_worklist, node, nd_work);
297270700Sjulian		mtx_exit(&ng_worklist_mtx, MTX_SPIN);
297370700Sjulian		/*
297470700Sjulian		 * We have the node. We also take over the reference
297570700Sjulian		 * that the list had on it.
297670700Sjulian		 * Now process as much as you can, until it won't
297770700Sjulian		 * let you have another item off the queue.
297870700Sjulian		 * All this time, keep the reference
297970700Sjulian		 * that lets us be sure that the node still exists.
298070700Sjulian		 * Let the reference go at the last minute.
298170700Sjulian		 */
298270700Sjulian		for (;;) {
298370784Sjulian			mtx_enter(&node->nd_input_queue.q_mtx, MTX_SPIN);
298470784Sjulian			item = ng_dequeue(&node->nd_input_queue);
298570700Sjulian			if (item == NULL) {
298670700Sjulian				/*
298770700Sjulian				 * Say we are on the queue as long as
298870700Sjulian				 * we are processing it here.
298970700Sjulian				 * it probably wouldn't come here while we
299070700Sjulian				 * are processing anyhow.
299170700Sjulian				 */
299270784Sjulian				node->nd_flags &= ~NG_WORKQ;
299370784Sjulian				mtx_exit(&node->nd_input_queue.q_mtx, MTX_SPIN);
299470784Sjulian				NG_NODE_UNREF(node);
299570700Sjulian				break; /* go look for another node */
299670700Sjulian			} else {
299770784Sjulian				mtx_exit(&node->nd_input_queue.q_mtx, MTX_SPIN);
299870784Sjulian#ifdef	NETGRAPH_DEBUG
299970700Sjulian        _ngi_check(item, __FILE__, __LINE__);
300070700Sjulian#endif
300170700Sjulian				ng_apply_item(node, item);
300270700Sjulian			}
300370700Sjulian		}
300452419Sjulian	}
300570700Sjulian}
300669922Sjulian
300770700Sjulianstatic void
300870700Sjulianng_worklist_remove(node_p node)
300970700Sjulian{
301070700Sjulian	mtx_enter(&ng_worklist_mtx, MTX_SPIN);
301170784Sjulian	if (node->nd_flags & NG_WORKQ) {
301270784Sjulian		TAILQ_REMOVE(&ng_worklist, node, nd_work);
301370784Sjulian		NG_NODE_UNREF(node);
301470700Sjulian	}
301570784Sjulian	node->nd_flags &= ~NG_WORKQ;
301670700Sjulian	mtx_exit(&ng_worklist_mtx, MTX_SPIN);
301770700Sjulian}
301870700Sjulian
301970700Sjulianstatic void
302070700Sjulianng_setisr(node_p node)
302170700Sjulian{
302270700Sjulian	mtx_enter(&ng_worklist_mtx, MTX_SPIN);
302370784Sjulian	if ((node->nd_flags & NG_WORKQ) == 0) {
302469922Sjulian		/*
302570700Sjulian		 * If we are not already on the work queue,
302670700Sjulian		 * then put us on.
302769922Sjulian		 */
302870784Sjulian		node->nd_flags |= NG_WORKQ;
302970784Sjulian		TAILQ_INSERT_TAIL(&ng_worklist, node, nd_work);
303070784Sjulian		NG_NODE_REF(node);
303170700Sjulian	}
303270700Sjulian	mtx_exit(&ng_worklist_mtx, MTX_SPIN);
303370700Sjulian	schednetisr(NETISR_NETGRAPH);
303470700Sjulian}
303570700Sjulian
303670700Sjulian
303770700Sjulian/***********************************************************************
303870700Sjulian* Externally useable functions to set up a queue item ready for sending
303970700Sjulian***********************************************************************/
304070700Sjulian
304170784Sjulian#ifdef	NETGRAPH_DEBUG
304270784Sjulian#define	ITEM_DEBUG_CHECKS						\
304370700Sjulian	do {								\
304470700Sjulian		if (item->el_dest ) {					\
304570700Sjulian			printf("item already has node");		\
304670700Sjulian			Debugger("has node");				\
304770784Sjulian			NG_NODE_UNREF(item->el_dest);			\
304870700Sjulian			item->el_dest = NULL;				\
304970700Sjulian		}							\
305070700Sjulian		if (item->el_hook ) {					\
305170700Sjulian			printf("item already has hook");		\
305270700Sjulian			Debugger("has hook");				\
305370784Sjulian			NG_HOOK_UNREF(item->el_hook);			\
305470700Sjulian			item->el_hook = NULL;				\
305570700Sjulian		}							\
305670700Sjulian	} while (0)
305770700Sjulian#else
305870784Sjulian#define ITEM_DEBUG_CHECKS
305970700Sjulian#endif
306070700Sjulian
306170700Sjulian/*
306270700Sjulian * Put elements into the item.
306370700Sjulian * Hook and node references will be removed when the item is dequeued.
306470700Sjulian * (or equivalent)
306570700Sjulian * (XXX) Unsafe because no reference held by peer on remote node.
306670700Sjulian * remote node might go away in this timescale.
306770700Sjulian * We know the hooks can't go away because that would require getting
306870700Sjulian * a writer item on both nodes and we must have at least a  reader
306970700Sjulian * here to eb able to do this.
307070700Sjulian * Note that the hook loaded is the REMOTE hook.
307170700Sjulian *
307270700Sjulian * This is possibly in the critical path for new data.
307370700Sjulian */
307470700Sjulianitem_p
307570700Sjulianng_package_data(struct mbuf *m, meta_p meta)
307670700Sjulian{
307770700Sjulian	item_p item;
307870700Sjulian
307970700Sjulian	if ((item = ng_getqblk()) == NULL) {
308070700Sjulian		NG_FREE_M(m);
308170700Sjulian		NG_FREE_META(meta);
308270700Sjulian		return (NULL);
308370700Sjulian	}
308470784Sjulian	ITEM_DEBUG_CHECKS;
308570700Sjulian	item->el_flags = NGQF_DATA;
308670700Sjulian	item->el_next = NULL;
308770700Sjulian	NGI_M(item) = m;
308870700Sjulian	NGI_META(item) = meta;
308970700Sjulian	return (item);
309070700Sjulian}
309170700Sjulian
309270700Sjulian/*
309370700Sjulian * Allocate a queue item and put items into it..
309470700Sjulian * Evaluate the address as this will be needed to queue it and
309570700Sjulian * to work out what some of the fields should be.
309670700Sjulian * Hook and node references will be removed when the item is dequeued.
309770700Sjulian * (or equivalent)
309870700Sjulian */
309970700Sjulianitem_p
310070700Sjulianng_package_msg(struct ng_mesg *msg)
310170700Sjulian{
310270700Sjulian	item_p item;
310370700Sjulian
310470700Sjulian	if ((item = ng_getqblk()) == NULL) {
310570700Sjulian		if ((msg->header.flags & NGF_STATIC) == 0) {
310670700Sjulian			NG_FREE_MSG(msg);
310769922Sjulian		}
310870700Sjulian		return (NULL);
310969922Sjulian	}
311070784Sjulian	ITEM_DEBUG_CHECKS;
311170700Sjulian	item->el_flags = NGQF_MESG;
311270700Sjulian	item->el_next = NULL;
311370700Sjulian	/*
311470700Sjulian	 * Set the current lasthook into the queue item
311570700Sjulian	 */
311670700Sjulian	NGI_MSG(item) = msg;
311770700Sjulian	NGI_RETADDR(item) = NULL;
311870700Sjulian	return (item);
311970700Sjulian}
312069922Sjulian
312170700Sjulian
312270700Sjulian
312370700Sjulian#define SET_RETADDR							\
312470700Sjulian	do {	/* Data items don't have retaddrs */			\
312570700Sjulian		if ((item->el_flags & NGQF_D_M) == NGQF_MESG) {		\
312670700Sjulian			if (retaddr) {					\
312770700Sjulian				NGI_RETADDR(item) = retaddr;		\
312870700Sjulian			} else {					\
312970700Sjulian				/*					\
313070700Sjulian				 * The old return address should be ok.	\
313170700Sjulian				 * If there isn't one, use the address	\
313270700Sjulian				 * here.				\
313370700Sjulian				 */					\
313470700Sjulian				if (NGI_RETADDR(item) == 0) {		\
313570700Sjulian					NGI_RETADDR(item)		\
313670700Sjulian						= ng_node2ID(here);	\
313770700Sjulian				}					\
313870700Sjulian			}						\
313970700Sjulian		}							\
314070700Sjulian	} while (0)
314170700Sjulian
314270700Sjulianint
314370700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
314470700Sjulian{
314570784Sjulian	ITEM_DEBUG_CHECKS;
314670700Sjulian	/*
314770700Sjulian	 * Quick sanity check..
314870784Sjulian	 * Since a hook holds a reference on it's node, once we know
314970784Sjulian	 * that the peer is still connected (even if invalid,) we know
315070784Sjulian	 * that the peer node is present, though maybe invalid.
315170700Sjulian	 */
315270700Sjulian	if ((hook == NULL)
315370784Sjulian	|| NG_HOOK_NOT_VALID(hook)
315470784Sjulian	|| (NG_HOOK_PEER(hook) == NULL)
315570784Sjulian	|| NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))
315670784Sjulian	|| NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) {
315770700Sjulian		NG_FREE_ITEM(item);
315870784Sjulian		TRAP_ERROR
315970700Sjulian		return (EINVAL);
316052419Sjulian	}
316152419Sjulian
316270700Sjulian	/*
316370700Sjulian	 * Transfer our interest to the other (peer) end.
316470700Sjulian	 */
316570784Sjulian	item->el_hook = NG_HOOK_PEER(hook);
316670784Sjulian	NG_HOOK_REF(item->el_hook); /* Don't let it go while on the queue */
316770784Sjulian	item->el_dest = NG_PEER_NODE(hook);
316870784Sjulian	NG_NODE_REF(item->el_dest); /* Nor this */
316970700Sjulian	SET_RETADDR;
317070700Sjulian	return (0);
317170700Sjulian}
317252419Sjulian
317370700Sjulianint
317470700Sjulianng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
317570700Sjulian{
317670700Sjulian	node_p  dest = NULL;
317770700Sjulian	hook_p	hook = NULL;
317870700Sjulian	int     error;
317970700Sjulian
318070784Sjulian	ITEM_DEBUG_CHECKS;
318170700Sjulian	/*
318270700Sjulian	 * Note that ng_path2noderef increments the reference count
318370700Sjulian	 * on the node for us if it finds one. So we don't have to.
318470700Sjulian	 */
318570700Sjulian	error = ng_path2noderef(here, address, &dest, &hook);
318670700Sjulian	if (error) {
318770700Sjulian		NG_FREE_ITEM(item);
318870784Sjulian		return (error);
318952419Sjulian	}
319070700Sjulian	item->el_dest = dest;
319170700Sjulian	if (( item->el_hook = hook))
319270784Sjulian		NG_HOOK_REF(hook);	/* don't let it go while on the queue */
319370700Sjulian	SET_RETADDR;
319470700Sjulian	return (0);
319570700Sjulian}
319652419Sjulian
319770700Sjulianint
319870700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
319970700Sjulian{
320070700Sjulian	node_p dest;
320170700Sjulian
320270784Sjulian	ITEM_DEBUG_CHECKS;
320370700Sjulian	/*
320470700Sjulian	 * Find the target node.
320570700Sjulian	 */
320670700Sjulian	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
320770700Sjulian	if (dest == NULL) {
320870700Sjulian		NG_FREE_ITEM(item);
320970784Sjulian		TRAP_ERROR
321070700Sjulian		return(EINVAL);
321170700Sjulian	}
321270700Sjulian	/* Fill out the contents */
321370700Sjulian	item->el_flags = NGQF_MESG;
321470700Sjulian	item->el_next = NULL;
321570700Sjulian	item->el_dest = dest;
321670700Sjulian	item->el_hook = NULL;
321770700Sjulian	SET_RETADDR;
321852419Sjulian	return (0);
321952419Sjulian}
322052419Sjulian
322152419Sjulian/*
322270700Sjulian * special case to send a message to self (e.g. destroy node)
322370700Sjulian * Possibly indicate an arrival hook too.
322470700Sjulian * Useful for removing that hook :-)
322552419Sjulian */
322670700Sjulianitem_p
322770700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
322852419Sjulian{
322970700Sjulian	item_p item;
323052419Sjulian
323170700Sjulian	/*
323270700Sjulian	 * Find the target node.
323370700Sjulian	 * If there is a HOOK argument, then use that in preference
323470700Sjulian	 * to the address.
323570700Sjulian	 */
323670700Sjulian	if ((item = ng_getqblk()) == NULL) {
323770700Sjulian		if ((msg->header.flags & NGF_STATIC) == 0) {
323870700Sjulian			NG_FREE_MSG(msg);
323952419Sjulian		}
324070700Sjulian		return (NULL);
324152419Sjulian	}
324270700Sjulian
324370700Sjulian	/* Fill out the contents */
324470700Sjulian	item->el_flags = NGQF_MESG;
324570700Sjulian	item->el_next = NULL;
324670700Sjulian	item->el_dest = here;
324770784Sjulian	NG_NODE_REF(here);
324870700Sjulian	item->el_hook = hook;
324970700Sjulian	if (hook)
325070784Sjulian		NG_HOOK_REF(hook);
325170700Sjulian	NGI_MSG(item) = msg;
325270700Sjulian	NGI_RETADDR(item) = ng_node2ID(here);
325370700Sjulian	return (item);
325452419Sjulian}
325552419Sjulian
325670700Sjulian/*
325770700Sjulian * Set the address, if none given, give the node here.
325870700Sjulian */
325970700Sjulianvoid
326070700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
326170700Sjulian{
326270700Sjulian	if (retaddr) {
326370700Sjulian		NGI_RETADDR(item) = retaddr;
326470700Sjulian	} else {
326570700Sjulian		/*
326670700Sjulian		 * The old return address should be ok.
326770700Sjulian		 * If there isn't one, use the address here.
326870700Sjulian		 */
326970700Sjulian		NGI_RETADDR(item) = ng_node2ID(here);
327070700Sjulian	}
327170700Sjulian}
327252419Sjulian
327370700Sjulian#define TESTING
327470700Sjulian#ifdef TESTING
327570700Sjulian/* just test all the macros */
327670700Sjulianvoid
327770700Sjulianng_macro_test(item_p item);
327870700Sjulianvoid
327970700Sjulianng_macro_test(item_p item)
328070700Sjulian{
328170700Sjulian	node_p node = NULL;
328270700Sjulian	hook_p hook = NULL;
328370700Sjulian	struct mbuf *m;
328470700Sjulian	meta_p meta;
328570700Sjulian	struct ng_mesg *msg;
328670700Sjulian	ng_ID_t retaddr;
328770700Sjulian	int	error;
328870700Sjulian
328970700Sjulian	NGI_GET_M(item, m);
329070700Sjulian	NGI_GET_META(item, meta);
329170700Sjulian	NGI_GET_MSG(item, msg);
329270700Sjulian	retaddr = NGI_RETADDR(item);
329370700Sjulian	NG_SEND_DATA(error, hook, m, meta);
329470700Sjulian	NG_SEND_DATA_ONLY(error, hook, m);
329570700Sjulian	NG_FWD_NEW_DATA(error, item, hook, m);
329670784Sjulian	NG_FWD_ITEM_HOOK(error, item, hook);
329770700Sjulian	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
329870700Sjulian	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
329970700Sjulian	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
330070700Sjulian	NG_QUEUE_MSG(error, node, msg, ".:", retaddr);
330170700Sjulian	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
330270700Sjulian}
330370700Sjulian#endif /* TESTING */
330470700Sjulian
3305