xref: /freebsd/sys/netgraph/ng_base.c (revision 069154d55f4e6cb7243ee37c5aa0391aa639e721)
14cf49a43SJulian Elischer /*
24cf49a43SJulian Elischer  * ng_base.c
34cf49a43SJulian Elischer  *
44cf49a43SJulian Elischer  * Copyright (c) 1996-1999 Whistle Communications, Inc.
54cf49a43SJulian Elischer  * All rights reserved.
64cf49a43SJulian Elischer  *
74cf49a43SJulian Elischer  * Subject to the following obligations and disclaimer of warranty, use and
84cf49a43SJulian Elischer  * redistribution of this software, in source or object code forms, with or
94cf49a43SJulian Elischer  * without modifications are expressly permitted by Whistle Communications;
104cf49a43SJulian Elischer  * provided, however, that:
114cf49a43SJulian Elischer  * 1. Any and all reproductions of the source or object code must include the
124cf49a43SJulian Elischer  *    copyright notice above and the following disclaimer of warranties; and
134cf49a43SJulian Elischer  * 2. No rights are granted, in any manner or form, to use Whistle
144cf49a43SJulian Elischer  *    Communications, Inc. trademarks, including the mark "WHISTLE
154cf49a43SJulian Elischer  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
164cf49a43SJulian Elischer  *    such appears in the above copyright notice or in the software.
174cf49a43SJulian Elischer  *
184cf49a43SJulian Elischer  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
194cf49a43SJulian Elischer  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
204cf49a43SJulian Elischer  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
214cf49a43SJulian Elischer  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
224cf49a43SJulian Elischer  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
234cf49a43SJulian Elischer  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
244cf49a43SJulian Elischer  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
254cf49a43SJulian Elischer  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
264cf49a43SJulian Elischer  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
274cf49a43SJulian Elischer  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
284cf49a43SJulian Elischer  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
294cf49a43SJulian Elischer  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
304cf49a43SJulian Elischer  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
314cf49a43SJulian Elischer  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
324cf49a43SJulian Elischer  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
334cf49a43SJulian Elischer  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
344cf49a43SJulian Elischer  * OF SUCH DAMAGE.
354cf49a43SJulian Elischer  *
36cc3bbd68SJulian Elischer  * Authors: Julian Elischer <julian@freebsd.org>
37cc3bbd68SJulian Elischer  *          Archie Cobbs <archie@freebsd.org>
384cf49a43SJulian Elischer  *
394cf49a43SJulian Elischer  * $FreeBSD$
404cf49a43SJulian Elischer  * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $
414cf49a43SJulian Elischer  */
424cf49a43SJulian Elischer 
434cf49a43SJulian Elischer /*
444cf49a43SJulian Elischer  * This file implements the base netgraph code.
454cf49a43SJulian Elischer  */
464cf49a43SJulian Elischer 
474cf49a43SJulian Elischer #include <sys/param.h>
484cf49a43SJulian Elischer #include <sys/systm.h>
494cf49a43SJulian Elischer #include <sys/errno.h>
504cf49a43SJulian Elischer #include <sys/kernel.h>
514cf49a43SJulian Elischer #include <sys/malloc.h>
524cf49a43SJulian Elischer #include <sys/syslog.h>
53069154d5SJulian Elischer #include <sys/sysctl.h>
544cf49a43SJulian Elischer #include <sys/linker.h>
554cf49a43SJulian Elischer #include <sys/queue.h>
564cf49a43SJulian Elischer #include <sys/mbuf.h>
575b664c7cSPoul-Henning Kamp #include <sys/ctype.h>
582b70adcbSArchie Cobbs #include <machine/limits.h>
594cf49a43SJulian Elischer 
604cf49a43SJulian Elischer #include <net/netisr.h>
614cf49a43SJulian Elischer 
624cf49a43SJulian Elischer #include <netgraph/ng_message.h>
634cf49a43SJulian Elischer #include <netgraph/netgraph.h>
64f8307e12SArchie Cobbs #include <netgraph/ng_parse.h>
654cf49a43SJulian Elischer 
6699ff8176SPeter Wemm MODULE_VERSION(netgraph, 1);
6799ff8176SPeter Wemm 
684cf49a43SJulian Elischer /* List of all nodes */
69069154d5SJulian Elischer static LIST_HEAD(, ng_node) ng_nodelist;
70069154d5SJulian Elischer static struct mtx	ng_nodelist_mtx;
71069154d5SJulian Elischer 
72069154d5SJulian Elischer /* NETISR queue */
73069154d5SJulian Elischer /* List nodes with unallocated work */
74069154d5SJulian Elischer static TAILQ_HEAD(, ng_node) ng_worklist = TAILQ_HEAD_INITIALIZER(ng_worklist);
75069154d5SJulian Elischer static struct mtx	ng_worklist_mtx;
764cf49a43SJulian Elischer 
774cf49a43SJulian Elischer /* List of installed types */
78069154d5SJulian Elischer static LIST_HEAD(, ng_type) ng_typelist;
79069154d5SJulian Elischer static struct mtx	ng_typelist_mtx;
804cf49a43SJulian Elischer 
81069154d5SJulian Elischer /* Hash related definitions */
82dc90cad9SJulian Elischer /* Don't nead to initialise them because it's a LIST */
83069154d5SJulian Elischer #define ID_HASH_SIZE 32 /* most systems wont need even this many */
84069154d5SJulian Elischer static LIST_HEAD(, ng_node) ng_ID_hash[ID_HASH_SIZE];
85069154d5SJulian Elischer static struct mtx	ng_idhash_mtx;
86069154d5SJulian Elischer 
87069154d5SJulian Elischer /* Mutex that protects the free queue item list */
88069154d5SJulian Elischer static volatile item_p		ngqfree;	/* free ones */
89069154d5SJulian Elischer static struct mtx	ngq_mtx;
90dc90cad9SJulian Elischer 
914cf49a43SJulian Elischer /* Internal functions */
924cf49a43SJulian Elischer static int	ng_add_hook(node_p node, const char *name, hook_p * hookp);
934cf49a43SJulian Elischer static int	ng_connect(hook_p hook1, hook_p hook2);
944cf49a43SJulian Elischer static void	ng_disconnect_hook(hook_p hook);
95069154d5SJulian Elischer static int	ng_generic_msg(node_p here, item_p item, hook_p lasthook);
96dc90cad9SJulian Elischer static ng_ID_t	ng_decodeidname(const char *name);
974cf49a43SJulian Elischer static int	ngb_mod_event(module_t mod, int event, void *data);
98069154d5SJulian Elischer static void	ng_worklist_remove(node_p node);
994cf49a43SJulian Elischer static void	ngintr(void);
100069154d5SJulian Elischer static int	ng_apply_item(node_p node, item_p item);
101069154d5SJulian Elischer static void	ng_flush_input_queue(struct ng_queue * ngq);
102069154d5SJulian Elischer static void	ng_setisr(node_p node);
103069154d5SJulian Elischer static node_p	ng_ID2noderef(ng_ID_t ID);
104069154d5SJulian Elischer 
105069154d5SJulian Elischer /* imported */
106069154d5SJulian Elischer int	ng_bypass(hook_p hook1, hook_p hook2);
107069154d5SJulian Elischer void	ng_cutlinks(node_p node);
108069154d5SJulian Elischer int	ng_con_nodes(node_p node, const char *name, node_p node2,
109069154d5SJulian Elischer 	const char *name2);
110069154d5SJulian Elischer void	ng_destroy_hook(hook_p hook);
111069154d5SJulian Elischer node_p	ng_name2noderef(node_p node, const char *name);
112069154d5SJulian Elischer int	ng_path2noderef(node_p here, const char *path,
113069154d5SJulian Elischer 	node_p *dest, hook_p *lasthook);
114069154d5SJulian Elischer struct	ng_type *ng_findtype(const char *type);
115069154d5SJulian Elischer int	ng_make_node(const char *type, node_p *nodepp);
116069154d5SJulian Elischer int	ng_mkpeer(node_p node, const char *name, const char *name2, char *type);
117069154d5SJulian Elischer int	ng_path_parse(char *addr, char **node, char **path, char **hook);
118069154d5SJulian Elischer void	ng_rmnode(node_p node);
119069154d5SJulian Elischer 
1204cf49a43SJulian Elischer 
1214cf49a43SJulian Elischer /* Our own netgraph malloc type */
1224cf49a43SJulian Elischer MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages");
123069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures");
124069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures");
125069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures");
126069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_META, "netgraph_meta", "netgraph name storage");
127069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage");
128069154d5SJulian Elischer 
129069154d5SJulian Elischer /* Should not be visible outside this file */
130069154d5SJulian Elischer #define NG_FREE_HOOK(hook) do { FREE((hook), M_NETGRAPH_HOOK); } while (0)
131069154d5SJulian Elischer #define NG_FREE_NODE(node) do { FREE((node), M_NETGRAPH_NODE); } while (0)
132069154d5SJulian Elischer #define NG_FREE_NAME(name) do { FREE((name), M_NETGRAPH_NAME); } while (0)
133069154d5SJulian Elischer /* Warning: Generally use NG_FREE_ITEM() instead */
134069154d5SJulian Elischer #define NG_FREE_ITEM_REAL(item) do { FREE((item), M_NETGRAPH_ITEM); } while (0)
135069154d5SJulian Elischer 
1364cf49a43SJulian Elischer 
1374cf49a43SJulian Elischer /* Set this to Debugger("X") to catch all errors as they occur */
1384cf49a43SJulian Elischer #ifndef TRAP_ERROR
1394cf49a43SJulian Elischer #define TRAP_ERROR
1404cf49a43SJulian Elischer #endif
1414cf49a43SJulian Elischer 
142dc90cad9SJulian Elischer static	ng_ID_t nextID = 1;
143dc90cad9SJulian Elischer 
144b2da83c2SArchie Cobbs #ifdef INVARIANTS
145b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)	do {					\
146b2da83c2SArchie Cobbs 		struct mbuf *n;						\
147b2da83c2SArchie Cobbs 		int total;						\
148b2da83c2SArchie Cobbs 									\
149b2da83c2SArchie Cobbs 		if (((m)->m_flags & M_PKTHDR) == 0)			\
150b2da83c2SArchie Cobbs 			panic("%s: !PKTHDR", __FUNCTION__);		\
151b2da83c2SArchie Cobbs 		for (total = 0, n = (m); n != NULL; n = n->m_next)	\
152b2da83c2SArchie Cobbs 			total += n->m_len;				\
153b2da83c2SArchie Cobbs 		if ((m)->m_pkthdr.len != total) {			\
154b2da83c2SArchie Cobbs 			panic("%s: %d != %d",				\
155b2da83c2SArchie Cobbs 			    __FUNCTION__, (m)->m_pkthdr.len, total);	\
156b2da83c2SArchie Cobbs 		}							\
157b2da83c2SArchie Cobbs 	} while (0)
158b2da83c2SArchie Cobbs #else
159b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m)
160b2da83c2SArchie Cobbs #endif
161b2da83c2SArchie Cobbs 
162dc90cad9SJulian Elischer 
1634cf49a43SJulian Elischer /************************************************************************
164f8307e12SArchie Cobbs 	Parse type definitions for generic messages
165f8307e12SArchie Cobbs ************************************************************************/
166f8307e12SArchie Cobbs 
167f8307e12SArchie Cobbs /* Handy structure parse type defining macro */
168f8307e12SArchie Cobbs #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args)				\
169f8307e12SArchie Cobbs static const struct ng_parse_struct_info				\
170f8307e12SArchie Cobbs 	ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args;	\
171f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_ ## lo ## _type = {	\
172f8307e12SArchie Cobbs 	&ng_parse_struct_type,						\
173f8307e12SArchie Cobbs 	&ng_ ## lo ## _type_info					\
174f8307e12SArchie Cobbs }
175f8307e12SArchie Cobbs 
176f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ());
177f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ());
178f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(name, NAME, ());
179f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ());
180f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ());
181f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ());
182f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type));
183f8307e12SArchie Cobbs 
184f8307e12SArchie Cobbs /* Get length of an array when the length is stored as a 32 bit
185f8307e12SArchie Cobbs    value immediately preceeding the array -- as with struct namelist
186f8307e12SArchie Cobbs    and struct typelist. */
187f8307e12SArchie Cobbs static int
188f8307e12SArchie Cobbs ng_generic_list_getLength(const struct ng_parse_type *type,
189f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
190f8307e12SArchie Cobbs {
191f8307e12SArchie Cobbs 	return *((const u_int32_t *)(buf - 4));
192f8307e12SArchie Cobbs }
193f8307e12SArchie Cobbs 
194f8307e12SArchie Cobbs /* Get length of the array of struct linkinfo inside a struct hooklist */
195f8307e12SArchie Cobbs static int
196f8307e12SArchie Cobbs ng_generic_linkinfo_getLength(const struct ng_parse_type *type,
197f8307e12SArchie Cobbs 	const u_char *start, const u_char *buf)
198f8307e12SArchie Cobbs {
199f8307e12SArchie Cobbs 	const struct hooklist *hl = (const struct hooklist *)start;
200f8307e12SArchie Cobbs 
201f8307e12SArchie Cobbs 	return hl->nodeinfo.hooks;
202f8307e12SArchie Cobbs }
203f8307e12SArchie Cobbs 
204f8307e12SArchie Cobbs /* Array type for a variable length array of struct namelist */
205f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_nodeinfoarray_type_info = {
206f8307e12SArchie Cobbs 	&ng_generic_nodeinfo_type,
207f8307e12SArchie Cobbs 	&ng_generic_list_getLength
208f8307e12SArchie Cobbs };
209f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_nodeinfoarray_type = {
210f8307e12SArchie Cobbs 	&ng_parse_array_type,
211f8307e12SArchie Cobbs 	&ng_nodeinfoarray_type_info
212f8307e12SArchie Cobbs };
213f8307e12SArchie Cobbs 
214f8307e12SArchie Cobbs /* Array type for a variable length array of struct typelist */
215f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_typeinfoarray_type_info = {
216f8307e12SArchie Cobbs 	&ng_generic_typeinfo_type,
217f8307e12SArchie Cobbs 	&ng_generic_list_getLength
218f8307e12SArchie Cobbs };
219f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_typeinfoarray_type = {
220f8307e12SArchie Cobbs 	&ng_parse_array_type,
221f8307e12SArchie Cobbs 	&ng_typeinfoarray_type_info
222f8307e12SArchie Cobbs };
223f8307e12SArchie Cobbs 
224f8307e12SArchie Cobbs /* Array type for array of struct linkinfo in struct hooklist */
225f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = {
226f8307e12SArchie Cobbs 	&ng_generic_linkinfo_type,
227f8307e12SArchie Cobbs 	&ng_generic_linkinfo_getLength
228f8307e12SArchie Cobbs };
229f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_linkinfo_array_type = {
230f8307e12SArchie Cobbs 	&ng_parse_array_type,
231f8307e12SArchie Cobbs 	&ng_generic_linkinfo_array_type_info
232f8307e12SArchie Cobbs };
233f8307e12SArchie Cobbs 
234f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type));
235f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST,
236f8307e12SArchie Cobbs 	(&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type));
237f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES,
238f8307e12SArchie Cobbs 	(&ng_generic_nodeinfoarray_type));
239f8307e12SArchie Cobbs 
240f8307e12SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */
241f8307e12SArchie Cobbs static const struct ng_cmdlist ng_generic_cmds[] = {
242f8307e12SArchie Cobbs 	{
243f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
244f8307e12SArchie Cobbs 	  NGM_SHUTDOWN,
245f8307e12SArchie Cobbs 	  "shutdown",
246f8307e12SArchie Cobbs 	  NULL,
247f8307e12SArchie Cobbs 	  NULL
248f8307e12SArchie Cobbs 	},
249f8307e12SArchie Cobbs 	{
250f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
251f8307e12SArchie Cobbs 	  NGM_MKPEER,
252f8307e12SArchie Cobbs 	  "mkpeer",
253f8307e12SArchie Cobbs 	  &ng_generic_mkpeer_type,
254f8307e12SArchie Cobbs 	  NULL
255f8307e12SArchie Cobbs 	},
256f8307e12SArchie Cobbs 	{
257f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
258f8307e12SArchie Cobbs 	  NGM_CONNECT,
259f8307e12SArchie Cobbs 	  "connect",
260f8307e12SArchie Cobbs 	  &ng_generic_connect_type,
261f8307e12SArchie Cobbs 	  NULL
262f8307e12SArchie Cobbs 	},
263f8307e12SArchie Cobbs 	{
264f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
265f8307e12SArchie Cobbs 	  NGM_NAME,
266f8307e12SArchie Cobbs 	  "name",
267f8307e12SArchie Cobbs 	  &ng_generic_name_type,
268f8307e12SArchie Cobbs 	  NULL
269f8307e12SArchie Cobbs 	},
270f8307e12SArchie Cobbs 	{
271f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
272f8307e12SArchie Cobbs 	  NGM_RMHOOK,
273f8307e12SArchie Cobbs 	  "rmhook",
274f8307e12SArchie Cobbs 	  &ng_generic_rmhook_type,
275f8307e12SArchie Cobbs 	  NULL
276f8307e12SArchie Cobbs 	},
277f8307e12SArchie Cobbs 	{
278f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
279f8307e12SArchie Cobbs 	  NGM_NODEINFO,
280f8307e12SArchie Cobbs 	  "nodeinfo",
281f8307e12SArchie Cobbs 	  NULL,
282f8307e12SArchie Cobbs 	  &ng_generic_nodeinfo_type
283f8307e12SArchie Cobbs 	},
284f8307e12SArchie Cobbs 	{
285f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
286f8307e12SArchie Cobbs 	  NGM_LISTHOOKS,
287f8307e12SArchie Cobbs 	  "listhooks",
288f8307e12SArchie Cobbs 	  NULL,
289f8307e12SArchie Cobbs 	  &ng_generic_hooklist_type
290f8307e12SArchie Cobbs 	},
291f8307e12SArchie Cobbs 	{
292f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
293f8307e12SArchie Cobbs 	  NGM_LISTNAMES,
294f8307e12SArchie Cobbs 	  "listnames",
295f8307e12SArchie Cobbs 	  NULL,
296f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type	/* same as NGM_LISTNODES */
297f8307e12SArchie Cobbs 	},
298f8307e12SArchie Cobbs 	{
299f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
300f8307e12SArchie Cobbs 	  NGM_LISTNODES,
301f8307e12SArchie Cobbs 	  "listnodes",
302f8307e12SArchie Cobbs 	  NULL,
303f8307e12SArchie Cobbs 	  &ng_generic_listnodes_type
304f8307e12SArchie Cobbs 	},
305f8307e12SArchie Cobbs 	{
306f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
307f8307e12SArchie Cobbs 	  NGM_LISTTYPES,
308f8307e12SArchie Cobbs 	  "listtypes",
309f8307e12SArchie Cobbs 	  NULL,
310f8307e12SArchie Cobbs 	  &ng_generic_typeinfo_type
311f8307e12SArchie Cobbs 	},
312f8307e12SArchie Cobbs 	{
313f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
3147095e097SPoul-Henning Kamp 	  NGM_TEXT_CONFIG,
3157095e097SPoul-Henning Kamp 	  "textconfig",
3167095e097SPoul-Henning Kamp 	  NULL,
3177095e097SPoul-Henning Kamp 	  &ng_parse_string_type
3187095e097SPoul-Henning Kamp 	},
3197095e097SPoul-Henning Kamp 	{
3207095e097SPoul-Henning Kamp 	  NGM_GENERIC_COOKIE,
321f8307e12SArchie Cobbs 	  NGM_TEXT_STATUS,
322f8307e12SArchie Cobbs 	  "textstatus",
323f8307e12SArchie Cobbs 	  NULL,
324f8307e12SArchie Cobbs 	  &ng_parse_string_type
325f8307e12SArchie Cobbs 	},
326f8307e12SArchie Cobbs 	{
327f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
328f8307e12SArchie Cobbs 	  NGM_ASCII2BINARY,
329f8307e12SArchie Cobbs 	  "ascii2binary",
330f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
331f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
332f8307e12SArchie Cobbs 	},
333f8307e12SArchie Cobbs 	{
334f8307e12SArchie Cobbs 	  NGM_GENERIC_COOKIE,
335f8307e12SArchie Cobbs 	  NGM_BINARY2ASCII,
336f8307e12SArchie Cobbs 	  "binary2ascii",
337f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type,
338f8307e12SArchie Cobbs 	  &ng_parse_ng_mesg_type
339f8307e12SArchie Cobbs 	},
340f8307e12SArchie Cobbs 	{ 0 }
341f8307e12SArchie Cobbs };
342f8307e12SArchie Cobbs 
343f8307e12SArchie Cobbs /************************************************************************
3444cf49a43SJulian Elischer 			Node routines
3454cf49a43SJulian Elischer ************************************************************************/
3464cf49a43SJulian Elischer 
3474cf49a43SJulian Elischer /*
3484cf49a43SJulian Elischer  * Instantiate a node of the requested type
3494cf49a43SJulian Elischer  */
3504cf49a43SJulian Elischer int
3514cf49a43SJulian Elischer ng_make_node(const char *typename, node_p *nodepp)
3524cf49a43SJulian Elischer {
3534cf49a43SJulian Elischer 	struct ng_type *type;
354069154d5SJulian Elischer 	int	error;
3554cf49a43SJulian Elischer 
3564cf49a43SJulian Elischer 	/* Check that the type makes sense */
3574cf49a43SJulian Elischer 	if (typename == NULL) {
3584cf49a43SJulian Elischer 		TRAP_ERROR;
3594cf49a43SJulian Elischer 		return (EINVAL);
3604cf49a43SJulian Elischer 	}
3614cf49a43SJulian Elischer 
3624cf49a43SJulian Elischer 	/* Locate the node type */
3634cf49a43SJulian Elischer 	if ((type = ng_findtype(typename)) == NULL) {
364cc3daab5SPeter Wemm 		char filename[NG_TYPELEN + 4];
3654cf49a43SJulian Elischer 		linker_file_t lf;
3664cf49a43SJulian Elischer 		int error;
3674cf49a43SJulian Elischer 
3684cf49a43SJulian Elischer 		/* Not found, try to load it as a loadable module */
369453b5565SJulian Elischer 		snprintf(filename, sizeof(filename), "ng_%s", typename);
370cc3daab5SPeter Wemm 		error = linker_load_file(filename, &lf);
3714cf49a43SJulian Elischer 		if (error != 0)
3724cf49a43SJulian Elischer 			return (error);
3734cf49a43SJulian Elischer 		lf->userrefs++;		/* pretend loaded by the syscall */
3744cf49a43SJulian Elischer 
3754cf49a43SJulian Elischer 		/* Try again, as now the type should have linked itself in */
3764cf49a43SJulian Elischer 		if ((type = ng_findtype(typename)) == NULL)
3774cf49a43SJulian Elischer 			return (ENXIO);
3784cf49a43SJulian Elischer 	}
3794cf49a43SJulian Elischer 
380069154d5SJulian Elischer 	/*
381069154d5SJulian Elischer 	 * If we have a constructor, then make the node and
382069154d5SJulian Elischer 	 * call the constructor to do type specific initialisation.
383069154d5SJulian Elischer 	 */
384069154d5SJulian Elischer 	if (type->constructor != NULL) {
385069154d5SJulian Elischer 		if ((error = ng_make_node_common(type, nodepp)) == 0) {
386069154d5SJulian Elischer 			if ((error = ((*type->constructor)(*nodepp)) != 0)) {
387069154d5SJulian Elischer 				ng_unref(*nodepp);
388069154d5SJulian Elischer 			}
389069154d5SJulian Elischer 		}
390069154d5SJulian Elischer 	} else {
391069154d5SJulian Elischer 		/*
392069154d5SJulian Elischer 		 * Node has no constructor. We cannot ask for one
393069154d5SJulian Elischer 		 * to be made. It must be brought into existance by
394069154d5SJulian Elischer 		 * some external agency. The external acency should
395069154d5SJulian Elischer 		 * call ng_make_node_common() directly to get the
396069154d5SJulian Elischer 		 * netgraph part initialised.
397069154d5SJulian Elischer 		 */
398069154d5SJulian Elischer 		error = EINVAL;
399069154d5SJulian Elischer 	}
400069154d5SJulian Elischer 	return (error);
4014cf49a43SJulian Elischer }
4024cf49a43SJulian Elischer 
4034cf49a43SJulian Elischer /*
404069154d5SJulian Elischer  * Generic node creation. Called by node initialisation for externally
405069154d5SJulian Elischer  * instantiated nodes (e.g. hardware, sockets, etc ).
4064cf49a43SJulian Elischer  * The returned node has a reference count of 1.
4074cf49a43SJulian Elischer  */
4084cf49a43SJulian Elischer int
4094cf49a43SJulian Elischer ng_make_node_common(struct ng_type *type, node_p *nodepp)
4104cf49a43SJulian Elischer {
4114cf49a43SJulian Elischer 	node_p node;
4124cf49a43SJulian Elischer 
4134cf49a43SJulian Elischer 	/* Require the node type to have been already installed */
4144cf49a43SJulian Elischer 	if (ng_findtype(type->name) == NULL) {
4154cf49a43SJulian Elischer 		TRAP_ERROR;
4164cf49a43SJulian Elischer 		return (EINVAL);
4174cf49a43SJulian Elischer 	}
4184cf49a43SJulian Elischer 
4194cf49a43SJulian Elischer 	/* Make a node and try attach it to the type */
420069154d5SJulian Elischer 	MALLOC(node, node_p, sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO);
4214cf49a43SJulian Elischer 	if (node == NULL) {
4224cf49a43SJulian Elischer 		TRAP_ERROR;
4234cf49a43SJulian Elischer 		return (ENOMEM);
4244cf49a43SJulian Elischer 	}
4254cf49a43SJulian Elischer 	node->type = type;
4264cf49a43SJulian Elischer 	node->refs++;				/* note reference */
4274cf49a43SJulian Elischer 	type->refs++;
4284cf49a43SJulian Elischer 
429069154d5SJulian Elischer 	mtx_init(&node->input_queue.q_mtx, "netgraph node mutex", 0);
430069154d5SJulian Elischer 	node->input_queue.queue = NULL;
431069154d5SJulian Elischer 	node->input_queue.last = &node->input_queue.queue;
432069154d5SJulian Elischer 	node->input_queue.q_flags = 0;
433069154d5SJulian Elischer 	node->input_queue.q_node = node;
4344cf49a43SJulian Elischer 
4354cf49a43SJulian Elischer 	/* Initialize hook list for new node */
4364cf49a43SJulian Elischer 	LIST_INIT(&node->hooks);
4374cf49a43SJulian Elischer 
438069154d5SJulian Elischer 	/* Link us into the node linked list */
439069154d5SJulian Elischer 	mtx_enter(&ng_nodelist_mtx, MTX_DEF);
440069154d5SJulian Elischer 	LIST_INSERT_HEAD(&ng_nodelist, node, nodes);
441069154d5SJulian Elischer 	mtx_exit(&ng_nodelist_mtx, MTX_DEF);
442069154d5SJulian Elischer 
443069154d5SJulian Elischer 
444dc90cad9SJulian Elischer 	/* get an ID and put us in the hash chain */
445069154d5SJulian Elischer 	mtx_enter(&ng_idhash_mtx, MTX_DEF);
446069154d5SJulian Elischer 	do { /* wrap protection, even if silly */
447069154d5SJulian Elischer 		node_p node2 = NULL;
448dc90cad9SJulian Elischer 		node->ID = nextID++; /* 137 per second for 1 year before wrap */
449069154d5SJulian Elischer 		if ((node->ID == 0) || (node2 = ng_ID2noderef(node->ID))) {
450069154d5SJulian Elischer 			if (node2) {
451069154d5SJulian Elischer 				ng_unref(node2);
452069154d5SJulian Elischer 				node2 = NULL;
453069154d5SJulian Elischer 			}
454069154d5SJulian Elischer 			continue;	/* try again */
455069154d5SJulian Elischer 		}
456069154d5SJulian Elischer 	} while (0);
457069154d5SJulian Elischer 	LIST_INSERT_HEAD(&ng_ID_hash[node->ID % ID_HASH_SIZE], node, idnodes);
458069154d5SJulian Elischer 	mtx_exit(&ng_idhash_mtx, MTX_DEF);
459dc90cad9SJulian Elischer 
4604cf49a43SJulian Elischer 	/* Done */
4614cf49a43SJulian Elischer 	*nodepp = node;
4624cf49a43SJulian Elischer 	return (0);
4634cf49a43SJulian Elischer }
4644cf49a43SJulian Elischer 
4654cf49a43SJulian Elischer /*
4664cf49a43SJulian Elischer  * Forceably start the shutdown process on a node. Either call
4674cf49a43SJulian Elischer  * it's shutdown method, or do the default shutdown if there is
4684cf49a43SJulian Elischer  * no type-specific method.
4694cf49a43SJulian Elischer  *
470069154d5SJulian Elischer  * We can only be called form a shutdown message, so we know we have
471069154d5SJulian Elischer  * a writer lock, and therefore exclusive access.
472069154d5SJulian Elischer  *
473069154d5SJulian Elischer  * Persistent node types must have a type-specific method which
474069154d5SJulian Elischer  * Allocates a new node. This one is irretrievably going away.
4754cf49a43SJulian Elischer  */
4764cf49a43SJulian Elischer void
4774cf49a43SJulian Elischer ng_rmnode(node_p node)
4784cf49a43SJulian Elischer {
4794cf49a43SJulian Elischer 	/* Check if it's already shutting down */
480069154d5SJulian Elischer 	if ((node->flags & NG_CLOSING) != 0)
4814cf49a43SJulian Elischer 		return;
4824cf49a43SJulian Elischer 
4834cf49a43SJulian Elischer 	/* Add an extra reference so it doesn't go away during this */
4844cf49a43SJulian Elischer 	node->refs++;
4854cf49a43SJulian Elischer 
4864cf49a43SJulian Elischer 	/* Mark it invalid so any newcomers know not to try use it */
487069154d5SJulian Elischer 	node->flags |= NG_INVALID|NG_CLOSING;
4884cf49a43SJulian Elischer 
4894cf49a43SJulian Elischer 	ng_unname(node);
4904cf49a43SJulian Elischer 	ng_cutlinks(node);
491069154d5SJulian Elischer 	/*
492069154d5SJulian Elischer 	 * Drain the input queue forceably.
493069154d5SJulian Elischer 	 */
494069154d5SJulian Elischer 	ng_flush_input_queue(&node->input_queue);
495069154d5SJulian Elischer 
496069154d5SJulian Elischer 	/*
497069154d5SJulian Elischer 	 * Take us off the work queue if we are there.
498069154d5SJulian Elischer 	 */
499069154d5SJulian Elischer 	ng_worklist_remove(node);
500069154d5SJulian Elischer 
501069154d5SJulian Elischer 
502069154d5SJulian Elischer 	/* Ask the type if it has anything to do in this case */
503069154d5SJulian Elischer 	if (node->type && node->type->shutdown) {
504069154d5SJulian Elischer 		(*node->type->shutdown)(node);
505069154d5SJulian Elischer 	} else {				/* do the default thing */
506069154d5SJulian Elischer 		ng_unref(node); /* XXX hmmmmm check this */
5074cf49a43SJulian Elischer 	}
5084cf49a43SJulian Elischer 
5094cf49a43SJulian Elischer 	/* Remove extra reference, possibly the last */
5104cf49a43SJulian Elischer 	ng_unref(node);
5114cf49a43SJulian Elischer }
5124cf49a43SJulian Elischer 
5134cf49a43SJulian Elischer /*
5144cf49a43SJulian Elischer  * Called by the destructor to remove any STANDARD external references
5154cf49a43SJulian Elischer  */
5164cf49a43SJulian Elischer void
5174cf49a43SJulian Elischer ng_cutlinks(node_p node)
5184cf49a43SJulian Elischer {
5194cf49a43SJulian Elischer 	hook_p  hook;
5204cf49a43SJulian Elischer 
5214cf49a43SJulian Elischer 	/* Make sure that this is set to stop infinite loops */
5224cf49a43SJulian Elischer 	node->flags |= NG_INVALID;
5234cf49a43SJulian Elischer 
524069154d5SJulian Elischer 	/*
525069154d5SJulian Elischer 	 * Drain the input queue forceably.
526069154d5SJulian Elischer 	 * We also do this in ng_rmnode
527069154d5SJulian Elischer 	 * to make sure we get all code paths.
528069154d5SJulian Elischer 	 */
529069154d5SJulian Elischer 	ng_flush_input_queue(&node->input_queue);
5304cf49a43SJulian Elischer 
5314cf49a43SJulian Elischer 	/* Notify all remaining connected nodes to disconnect */
5324cf49a43SJulian Elischer 	while ((hook = LIST_FIRST(&node->hooks)) != NULL)
5334cf49a43SJulian Elischer 		ng_destroy_hook(hook);
5344cf49a43SJulian Elischer }
5354cf49a43SJulian Elischer 
5364cf49a43SJulian Elischer /*
5374cf49a43SJulian Elischer  * Remove a reference to the node, possibly the last
5384cf49a43SJulian Elischer  */
5394cf49a43SJulian Elischer void
5404cf49a43SJulian Elischer ng_unref(node_p node)
5414cf49a43SJulian Elischer {
542e8a49db2SJulian Elischer 	int	s;
543e8a49db2SJulian Elischer 
544e8a49db2SJulian Elischer 	s = splhigh();
545069154d5SJulian Elischer /* XXX not atomic.. fix */
5464cf49a43SJulian Elischer 	if (--node->refs <= 0) {
547069154d5SJulian Elischer 
548069154d5SJulian Elischer 		mtx_enter(&ng_nodelist_mtx, MTX_DEF);
549069154d5SJulian Elischer 		node->type->refs--; /* XXX maybe should get types lock? */
5504cf49a43SJulian Elischer 		LIST_REMOVE(node, nodes);
551069154d5SJulian Elischer 		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
552069154d5SJulian Elischer 
553069154d5SJulian Elischer 		mtx_enter(&ng_idhash_mtx, MTX_DEF);
554dc90cad9SJulian Elischer 		LIST_REMOVE(node, idnodes);
555069154d5SJulian Elischer 		mtx_exit(&ng_idhash_mtx, MTX_DEF);
556069154d5SJulian Elischer 
557069154d5SJulian Elischer 		NG_FREE_NODE(node);
5584cf49a43SJulian Elischer 	}
559e8a49db2SJulian Elischer 	splx(s);
5604cf49a43SJulian Elischer }
5614cf49a43SJulian Elischer 
5624cf49a43SJulian Elischer /************************************************************************
563dc90cad9SJulian Elischer 			Node ID handling
564dc90cad9SJulian Elischer ************************************************************************/
565dc90cad9SJulian Elischer static node_p
566069154d5SJulian Elischer ng_ID2noderef(ng_ID_t ID)
567dc90cad9SJulian Elischer {
568dc90cad9SJulian Elischer 	node_p np;
569069154d5SJulian Elischer 	mtx_enter(&ng_idhash_mtx, MTX_DEF);
570069154d5SJulian Elischer 	LIST_FOREACH(np, &ng_ID_hash[ID % ID_HASH_SIZE], idnodes) {
571dc90cad9SJulian Elischer 		if (np->ID == ID)
572dc90cad9SJulian Elischer 			break;
573dc90cad9SJulian Elischer 	}
574069154d5SJulian Elischer 	if(np)
575069154d5SJulian Elischer 		np->refs++;
576069154d5SJulian Elischer 	mtx_exit(&ng_idhash_mtx, MTX_DEF);
577dc90cad9SJulian Elischer 	return(np);
578dc90cad9SJulian Elischer }
579dc90cad9SJulian Elischer 
580dc90cad9SJulian Elischer ng_ID_t
581dc90cad9SJulian Elischer ng_node2ID(node_p node)
582dc90cad9SJulian Elischer {
583069154d5SJulian Elischer 	return (node?node->ID:0);
584dc90cad9SJulian Elischer }
585dc90cad9SJulian Elischer 
586dc90cad9SJulian Elischer /************************************************************************
5874cf49a43SJulian Elischer 			Node name handling
5884cf49a43SJulian Elischer ************************************************************************/
5894cf49a43SJulian Elischer 
5904cf49a43SJulian Elischer /*
5914cf49a43SJulian Elischer  * Assign a node a name. Once assigned, the name cannot be changed.
5924cf49a43SJulian Elischer  */
5934cf49a43SJulian Elischer int
5944cf49a43SJulian Elischer ng_name_node(node_p node, const char *name)
5954cf49a43SJulian Elischer {
5964cf49a43SJulian Elischer 	int i;
597069154d5SJulian Elischer 	node_p node2;
5984cf49a43SJulian Elischer 
5994cf49a43SJulian Elischer 	/* Check the name is valid */
6004cf49a43SJulian Elischer 	for (i = 0; i < NG_NODELEN + 1; i++) {
6014cf49a43SJulian Elischer 		if (name[i] == '\0' || name[i] == '.' || name[i] == ':')
6024cf49a43SJulian Elischer 			break;
6034cf49a43SJulian Elischer 	}
6044cf49a43SJulian Elischer 	if (i == 0 || name[i] != '\0') {
6054cf49a43SJulian Elischer 		TRAP_ERROR;
6064cf49a43SJulian Elischer 		return (EINVAL);
6074cf49a43SJulian Elischer 	}
608dc90cad9SJulian Elischer 	if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */
6094cf49a43SJulian Elischer 		TRAP_ERROR;
6104cf49a43SJulian Elischer 		return (EINVAL);
6114cf49a43SJulian Elischer 	}
6124cf49a43SJulian Elischer 
6134cf49a43SJulian Elischer 	/* Check the name isn't already being used */
614069154d5SJulian Elischer 	if ((node2 = ng_name2noderef(node, name)) != NULL) {
615069154d5SJulian Elischer 		ng_unref(node2);
6164cf49a43SJulian Elischer 		TRAP_ERROR;
6174cf49a43SJulian Elischer 		return (EADDRINUSE);
6184cf49a43SJulian Elischer 	}
6194cf49a43SJulian Elischer 
620069154d5SJulian Elischer 	/* copy it */
6214cf49a43SJulian Elischer 	strcpy(node->name, name);
6224cf49a43SJulian Elischer 
6234cf49a43SJulian Elischer 	return (0);
6244cf49a43SJulian Elischer }
6254cf49a43SJulian Elischer 
6264cf49a43SJulian Elischer /*
6274cf49a43SJulian Elischer  * Find a node by absolute name. The name should NOT end with ':'
6284cf49a43SJulian Elischer  * The name "." means "this node" and "[xxx]" means "the node
6294cf49a43SJulian Elischer  * with ID (ie, at address) xxx".
6304cf49a43SJulian Elischer  *
6314cf49a43SJulian Elischer  * Returns the node if found, else NULL.
632069154d5SJulian Elischer  * Eventually should add something faster than a sequential search.
633069154d5SJulian Elischer  * Note it holds a reference on the node so you an be sure it's still there.
6344cf49a43SJulian Elischer  */
6354cf49a43SJulian Elischer node_p
636069154d5SJulian Elischer ng_name2noderef(node_p here, const char *name)
6374cf49a43SJulian Elischer {
638dc90cad9SJulian Elischer 	node_p node;
639dc90cad9SJulian Elischer 	ng_ID_t temp;
6404cf49a43SJulian Elischer 
6414cf49a43SJulian Elischer 	/* "." means "this node" */
642069154d5SJulian Elischer 	if (strcmp(name, ".") == 0) {
643069154d5SJulian Elischer 		here->refs++;
644069154d5SJulian Elischer 		return(here);
645069154d5SJulian Elischer 	}
6464cf49a43SJulian Elischer 
6474cf49a43SJulian Elischer 	/* Check for name-by-ID */
648dc90cad9SJulian Elischer 	if ((temp = ng_decodeidname(name)) != 0) {
649069154d5SJulian Elischer 		return (ng_ID2noderef(temp));
6504cf49a43SJulian Elischer 	}
6514cf49a43SJulian Elischer 
6524cf49a43SJulian Elischer 	/* Find node by name */
653069154d5SJulian Elischer 	mtx_enter(&ng_nodelist_mtx, MTX_DEF);
654069154d5SJulian Elischer 	LIST_FOREACH(node, &ng_nodelist, nodes) {
655069154d5SJulian Elischer 		if (node->name[0] != '\0' && strcmp(node->name, name) == 0)
6564cf49a43SJulian Elischer 			break;
6574cf49a43SJulian Elischer 	}
658069154d5SJulian Elischer 	if (node)
659069154d5SJulian Elischer 		node->refs++;
660069154d5SJulian Elischer 	mtx_exit(&ng_nodelist_mtx, MTX_DEF);
6614cf49a43SJulian Elischer 	return (node);
6624cf49a43SJulian Elischer }
6634cf49a43SJulian Elischer 
6644cf49a43SJulian Elischer /*
665dc90cad9SJulian Elischer  * Decode a ID name, eg. "[f03034de]". Returns 0 if the
666dc90cad9SJulian Elischer  * string is not valid, otherwise returns the value.
6674cf49a43SJulian Elischer  */
668dc90cad9SJulian Elischer static ng_ID_t
6694cf49a43SJulian Elischer ng_decodeidname(const char *name)
6704cf49a43SJulian Elischer {
6712b70adcbSArchie Cobbs 	const int len = strlen(name);
67225792ef3SArchie Cobbs 	char *eptr;
6732b70adcbSArchie Cobbs 	u_long val;
6744cf49a43SJulian Elischer 
6752b70adcbSArchie Cobbs 	/* Check for proper length, brackets, no leading junk */
6762b70adcbSArchie Cobbs 	if (len < 3 || name[0] != '[' || name[len - 1] != ']'
677ef050c81SJulian Elischer 	    || !isxdigit(name[1]))
6782b70adcbSArchie Cobbs 		return (0);
6794cf49a43SJulian Elischer 
6802b70adcbSArchie Cobbs 	/* Decode number */
6812b70adcbSArchie Cobbs 	val = strtoul(name + 1, &eptr, 16);
682ef050c81SJulian Elischer 	if (eptr - name != len - 1 || val == ULONG_MAX || val == 0)
68312f035e0SJulian Elischer 		return ((ng_ID_t)0);
6842b70adcbSArchie Cobbs 	return (ng_ID_t)val;
6854cf49a43SJulian Elischer }
6864cf49a43SJulian Elischer 
6874cf49a43SJulian Elischer /*
6884cf49a43SJulian Elischer  * Remove a name from a node. This should only be called
6894cf49a43SJulian Elischer  * when shutting down and removing the node.
6904cf49a43SJulian Elischer  */
6914cf49a43SJulian Elischer void
6924cf49a43SJulian Elischer ng_unname(node_p node)
6934cf49a43SJulian Elischer {
694069154d5SJulian Elischer 	bzero(node->name, NG_NODELEN);
6954cf49a43SJulian Elischer }
6964cf49a43SJulian Elischer 
6974cf49a43SJulian Elischer /************************************************************************
6984cf49a43SJulian Elischer 			Hook routines
6994cf49a43SJulian Elischer  Names are not optional. Hooks are always connected, except for a
7004cf49a43SJulian Elischer  brief moment within these routines.
7014cf49a43SJulian Elischer ************************************************************************/
7024cf49a43SJulian Elischer 
7034cf49a43SJulian Elischer /*
7044cf49a43SJulian Elischer  * Remove a hook reference
7054cf49a43SJulian Elischer  */
7064cf49a43SJulian Elischer static void
7074cf49a43SJulian Elischer ng_unref_hook(hook_p hook)
7084cf49a43SJulian Elischer {
709e8a49db2SJulian Elischer 	int	s;
710e8a49db2SJulian Elischer 
711e8a49db2SJulian Elischer 	s = splhigh();
712069154d5SJulian Elischer /* XXX not atomic.. fix */
713069154d5SJulian Elischer 	if (--hook->refs == 0) {
714069154d5SJulian Elischer 		if (hook->node) {
715069154d5SJulian Elischer 			ng_unref(hook->node);
716069154d5SJulian Elischer 			hook->node = NULL;
717069154d5SJulian Elischer 		}
718069154d5SJulian Elischer 		NG_FREE_HOOK(hook);
719069154d5SJulian Elischer 	}
720e8a49db2SJulian Elischer 	splx(s);
7214cf49a43SJulian Elischer }
7224cf49a43SJulian Elischer 
7234cf49a43SJulian Elischer /*
7244cf49a43SJulian Elischer  * Add an unconnected hook to a node. Only used internally.
7254cf49a43SJulian Elischer  */
7264cf49a43SJulian Elischer static int
7274cf49a43SJulian Elischer ng_add_hook(node_p node, const char *name, hook_p *hookp)
7284cf49a43SJulian Elischer {
7294cf49a43SJulian Elischer 	hook_p hook;
7304cf49a43SJulian Elischer 	int error = 0;
7314cf49a43SJulian Elischer 
7324cf49a43SJulian Elischer 	/* Check that the given name is good */
7334cf49a43SJulian Elischer 	if (name == NULL) {
7344cf49a43SJulian Elischer 		TRAP_ERROR;
7354cf49a43SJulian Elischer 		return (EINVAL);
7364cf49a43SJulian Elischer 	}
737899e9c4eSArchie Cobbs 	if (ng_findhook(node, name) != NULL) {
7384cf49a43SJulian Elischer 		TRAP_ERROR;
7394cf49a43SJulian Elischer 		return (EEXIST);
7404cf49a43SJulian Elischer 	}
7414cf49a43SJulian Elischer 
7424cf49a43SJulian Elischer 	/* Allocate the hook and link it up */
743069154d5SJulian Elischer 	MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO);
7444cf49a43SJulian Elischer 	if (hook == NULL) {
7454cf49a43SJulian Elischer 		TRAP_ERROR;
7464cf49a43SJulian Elischer 		return (ENOMEM);
7474cf49a43SJulian Elischer 	}
7484cf49a43SJulian Elischer 	hook->refs = 1;
7494cf49a43SJulian Elischer 	hook->flags = HK_INVALID;
7504cf49a43SJulian Elischer 	hook->node = node;
7514cf49a43SJulian Elischer 	node->refs++;		/* each hook counts as a reference */
7524cf49a43SJulian Elischer 
7534cf49a43SJulian Elischer 	/* Check if the node type code has something to say about it */
7544cf49a43SJulian Elischer 	if (node->type->newhook != NULL)
755069154d5SJulian Elischer 		if ((error = (*node->type->newhook)(node, hook, name)) != 0) {
756069154d5SJulian Elischer 			ng_unref_hook(hook);	/* this frees the hook */
757069154d5SJulian Elischer 			return (error);
758069154d5SJulian Elischer 		}
7594cf49a43SJulian Elischer 
7604cf49a43SJulian Elischer 	/*
7614cf49a43SJulian Elischer 	 * The 'type' agrees so far, so go ahead and link it in.
7624cf49a43SJulian Elischer 	 * We'll ask again later when we actually connect the hooks.
763069154d5SJulian Elischer 	 * The reference we have is for this linkage.
7644cf49a43SJulian Elischer 	 */
7654cf49a43SJulian Elischer 	LIST_INSERT_HEAD(&node->hooks, hook, hooks);
7664cf49a43SJulian Elischer 	node->numhooks++;
7674cf49a43SJulian Elischer 
7684cf49a43SJulian Elischer 	/* Set hook name */
7694cf49a43SJulian Elischer 	strcpy(hook->name, name);
7704cf49a43SJulian Elischer 	if (hookp)
7714cf49a43SJulian Elischer 		*hookp = hook;
7724cf49a43SJulian Elischer 	return (error);
7734cf49a43SJulian Elischer }
7744cf49a43SJulian Elischer 
7754cf49a43SJulian Elischer /*
7764cf49a43SJulian Elischer  * Connect a pair of hooks. Only used internally.
7774cf49a43SJulian Elischer  */
7784cf49a43SJulian Elischer static int
7794cf49a43SJulian Elischer ng_connect(hook_p hook1, hook_p hook2)
7804cf49a43SJulian Elischer {
7814cf49a43SJulian Elischer 	int     error;
7824cf49a43SJulian Elischer 
7834cf49a43SJulian Elischer 	hook1->peer = hook2;
7844cf49a43SJulian Elischer 	hook2->peer = hook1;
7854cf49a43SJulian Elischer 
7864cf49a43SJulian Elischer 	/* Give each node the opportunity to veto the impending connection */
7874cf49a43SJulian Elischer 	if (hook1->node->type->connect) {
7884cf49a43SJulian Elischer 		if ((error = (*hook1->node->type->connect) (hook1))) {
7894cf49a43SJulian Elischer 			ng_destroy_hook(hook1);	/* also zaps hook2 */
7904cf49a43SJulian Elischer 			return (error);
7914cf49a43SJulian Elischer 		}
7924cf49a43SJulian Elischer 	}
7934cf49a43SJulian Elischer 	if (hook2->node->type->connect) {
7944cf49a43SJulian Elischer 		if ((error = (*hook2->node->type->connect) (hook2))) {
7954cf49a43SJulian Elischer 			ng_destroy_hook(hook2);	/* also zaps hook1 */
7964cf49a43SJulian Elischer 			return (error);
7974cf49a43SJulian Elischer 		}
7984cf49a43SJulian Elischer 	}
7994cf49a43SJulian Elischer 	hook1->flags &= ~HK_INVALID;
8004cf49a43SJulian Elischer 	hook2->flags &= ~HK_INVALID;
8014cf49a43SJulian Elischer 	return (0);
8024cf49a43SJulian Elischer }
8034cf49a43SJulian Elischer 
8044cf49a43SJulian Elischer /*
805899e9c4eSArchie Cobbs  * Find a hook
806899e9c4eSArchie Cobbs  *
807899e9c4eSArchie Cobbs  * Node types may supply their own optimized routines for finding
808899e9c4eSArchie Cobbs  * hooks.  If none is supplied, we just do a linear search.
809899e9c4eSArchie Cobbs  */
810899e9c4eSArchie Cobbs hook_p
811899e9c4eSArchie Cobbs ng_findhook(node_p node, const char *name)
812899e9c4eSArchie Cobbs {
813899e9c4eSArchie Cobbs 	hook_p hook;
814899e9c4eSArchie Cobbs 
815899e9c4eSArchie Cobbs 	if (node->type->findhook != NULL)
816899e9c4eSArchie Cobbs 		return (*node->type->findhook)(node, name);
817899e9c4eSArchie Cobbs 	LIST_FOREACH(hook, &node->hooks, hooks) {
818069154d5SJulian Elischer 		if (strcmp(hook->name, name) == 0)
819899e9c4eSArchie Cobbs 			return (hook);
820899e9c4eSArchie Cobbs 	}
821899e9c4eSArchie Cobbs 	return (NULL);
822899e9c4eSArchie Cobbs }
823899e9c4eSArchie Cobbs 
824899e9c4eSArchie Cobbs /*
8254cf49a43SJulian Elischer  * Destroy a hook
8264cf49a43SJulian Elischer  *
8274cf49a43SJulian Elischer  * As hooks are always attached, this really destroys two hooks.
8284cf49a43SJulian Elischer  * The one given, and the one attached to it. Disconnect the hooks
8294cf49a43SJulian Elischer  * from each other first.
8304cf49a43SJulian Elischer  */
8314cf49a43SJulian Elischer void
8324cf49a43SJulian Elischer ng_destroy_hook(hook_p hook)
8334cf49a43SJulian Elischer {
8344cf49a43SJulian Elischer 	hook_p peer = hook->peer;
8354cf49a43SJulian Elischer 
8364cf49a43SJulian Elischer 	hook->flags |= HK_INVALID;		/* as soon as possible */
8374cf49a43SJulian Elischer 	if (peer) {
8384cf49a43SJulian Elischer 		peer->flags |= HK_INVALID;	/* as soon as possible */
8394cf49a43SJulian Elischer 		hook->peer = NULL;
8404cf49a43SJulian Elischer 		peer->peer = NULL;
8414cf49a43SJulian Elischer 		ng_disconnect_hook(peer);
8424cf49a43SJulian Elischer 	}
8434cf49a43SJulian Elischer 	ng_disconnect_hook(hook);
8444cf49a43SJulian Elischer }
8454cf49a43SJulian Elischer 
8464cf49a43SJulian Elischer /*
8474cf49a43SJulian Elischer  * Notify the node of the hook's demise. This may result in more actions
8484cf49a43SJulian Elischer  * (e.g. shutdown) but we don't do that ourselves and don't know what
8494cf49a43SJulian Elischer  * happens there. If there is no appropriate handler, then just remove it
8504cf49a43SJulian Elischer  * (and decrement the reference count of it's node which in turn might
8514cf49a43SJulian Elischer  * make something happen).
8524cf49a43SJulian Elischer  */
8534cf49a43SJulian Elischer static void
8544cf49a43SJulian Elischer ng_disconnect_hook(hook_p hook)
8554cf49a43SJulian Elischer {
8564cf49a43SJulian Elischer 	node_p node = hook->node;
8574cf49a43SJulian Elischer 
8584cf49a43SJulian Elischer 	/*
8594cf49a43SJulian Elischer 	 * Remove the hook from the node's list to avoid possible recursion
8604cf49a43SJulian Elischer 	 * in case the disconnection results in node shutdown.
8614cf49a43SJulian Elischer 	 */
8624cf49a43SJulian Elischer 	LIST_REMOVE(hook, hooks);
8634cf49a43SJulian Elischer 	node->numhooks--;
8644cf49a43SJulian Elischer 	if (node->type->disconnect) {
8654cf49a43SJulian Elischer 		/*
8664cf49a43SJulian Elischer 		 * The type handler may elect to destroy the peer so don't
8674cf49a43SJulian Elischer 		 * trust its existance after this point.
8684cf49a43SJulian Elischer 		 */
8694cf49a43SJulian Elischer 		(*node->type->disconnect) (hook);
8704cf49a43SJulian Elischer 	}
8714cf49a43SJulian Elischer 	ng_unref_hook(hook);
8724cf49a43SJulian Elischer }
8734cf49a43SJulian Elischer 
8744cf49a43SJulian Elischer /*
8754cf49a43SJulian Elischer  * Take two hooks on a node and merge the connection so that the given node
8764cf49a43SJulian Elischer  * is effectively bypassed.
8774cf49a43SJulian Elischer  */
8784cf49a43SJulian Elischer int
8794cf49a43SJulian Elischer ng_bypass(hook_p hook1, hook_p hook2)
8804cf49a43SJulian Elischer {
8814cf49a43SJulian Elischer 	if (hook1->node != hook2->node)
8824cf49a43SJulian Elischer 		return (EINVAL);
8834cf49a43SJulian Elischer 	hook1->peer->peer = hook2->peer;
8844cf49a43SJulian Elischer 	hook2->peer->peer = hook1->peer;
8854cf49a43SJulian Elischer 
8864cf49a43SJulian Elischer 	/* XXX If we ever cache methods on hooks update them as well */
8874cf49a43SJulian Elischer 	hook1->peer = NULL;
8884cf49a43SJulian Elischer 	hook2->peer = NULL;
8894cf49a43SJulian Elischer 	ng_destroy_hook(hook1);
8904cf49a43SJulian Elischer 	ng_destroy_hook(hook2);
8914cf49a43SJulian Elischer 	return (0);
8924cf49a43SJulian Elischer }
8934cf49a43SJulian Elischer 
8944cf49a43SJulian Elischer /*
8954cf49a43SJulian Elischer  * Install a new netgraph type
8964cf49a43SJulian Elischer  */
8974cf49a43SJulian Elischer int
8984cf49a43SJulian Elischer ng_newtype(struct ng_type *tp)
8994cf49a43SJulian Elischer {
9004cf49a43SJulian Elischer 	const size_t namelen = strlen(tp->name);
9014cf49a43SJulian Elischer 
9024cf49a43SJulian Elischer 	/* Check version and type name fields */
903589f6ed8SJulian Elischer 	if ((tp->version != NG_ABI_VERSION)
904589f6ed8SJulian Elischer 	|| (namelen == 0)
905589f6ed8SJulian Elischer 	|| (namelen > NG_TYPELEN)) {
9064cf49a43SJulian Elischer 		TRAP_ERROR;
9074cf49a43SJulian Elischer 		return (EINVAL);
9084cf49a43SJulian Elischer 	}
9094cf49a43SJulian Elischer 
9104cf49a43SJulian Elischer 	/* Check for name collision */
9114cf49a43SJulian Elischer 	if (ng_findtype(tp->name) != NULL) {
9124cf49a43SJulian Elischer 		TRAP_ERROR;
9134cf49a43SJulian Elischer 		return (EEXIST);
9144cf49a43SJulian Elischer 	}
9154cf49a43SJulian Elischer 
9164cf49a43SJulian Elischer 	tp->refs = 0;
917069154d5SJulian Elischer 
918069154d5SJulian Elischer 	/* Link in new type */
919069154d5SJulian Elischer 	mtx_enter(&ng_typelist_mtx, MTX_DEF);
920069154d5SJulian Elischer 	LIST_INSERT_HEAD(&ng_typelist, tp, types);
921069154d5SJulian Elischer 	mtx_exit(&ng_typelist_mtx, MTX_DEF);
9224cf49a43SJulian Elischer 	return (0);
9234cf49a43SJulian Elischer }
9244cf49a43SJulian Elischer 
9254cf49a43SJulian Elischer /*
9264cf49a43SJulian Elischer  * Look for a type of the name given
9274cf49a43SJulian Elischer  */
9284cf49a43SJulian Elischer struct ng_type *
9294cf49a43SJulian Elischer ng_findtype(const char *typename)
9304cf49a43SJulian Elischer {
9314cf49a43SJulian Elischer 	struct ng_type *type;
9324cf49a43SJulian Elischer 
933069154d5SJulian Elischer 	mtx_enter(&ng_typelist_mtx, MTX_DEF);
934069154d5SJulian Elischer 	LIST_FOREACH(type, &ng_typelist, types) {
9354cf49a43SJulian Elischer 		if (strcmp(type->name, typename) == 0)
9364cf49a43SJulian Elischer 			break;
9374cf49a43SJulian Elischer 	}
938069154d5SJulian Elischer 	mtx_exit(&ng_typelist_mtx, MTX_DEF);
9394cf49a43SJulian Elischer 	return (type);
9404cf49a43SJulian Elischer }
9414cf49a43SJulian Elischer 
9424cf49a43SJulian Elischer /************************************************************************
9434cf49a43SJulian Elischer 			Composite routines
9444cf49a43SJulian Elischer ************************************************************************/
9454cf49a43SJulian Elischer 
9464cf49a43SJulian Elischer /*
9474cf49a43SJulian Elischer  * Make a peer and connect. The order is arranged to minimise
9484cf49a43SJulian Elischer  * the work needed to back out in case of error.
9494cf49a43SJulian Elischer  */
9504cf49a43SJulian Elischer int
9514cf49a43SJulian Elischer ng_mkpeer(node_p node, const char *name, const char *name2, char *type)
9524cf49a43SJulian Elischer {
9534cf49a43SJulian Elischer 	node_p  node2;
9544cf49a43SJulian Elischer 	hook_p  hook;
9554cf49a43SJulian Elischer 	hook_p  hook2;
9564cf49a43SJulian Elischer 	int     error;
9574cf49a43SJulian Elischer 
9584cf49a43SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook)))
9594cf49a43SJulian Elischer 		return (error);
9604cf49a43SJulian Elischer 	if ((error = ng_make_node(type, &node2))) {
9614cf49a43SJulian Elischer 		ng_destroy_hook(hook);
9624cf49a43SJulian Elischer 		return (error);
9634cf49a43SJulian Elischer 	}
9644cf49a43SJulian Elischer 	if ((error = ng_add_hook(node2, name2, &hook2))) {
9654cf49a43SJulian Elischer 		ng_rmnode(node2);
9664cf49a43SJulian Elischer 		ng_destroy_hook(hook);
9674cf49a43SJulian Elischer 		return (error);
9684cf49a43SJulian Elischer 	}
9694cf49a43SJulian Elischer 
9704cf49a43SJulian Elischer 	/*
9714cf49a43SJulian Elischer 	 * Actually link the two hooks together.. on failure they are
9724cf49a43SJulian Elischer 	 * destroyed so we don't have to do that here.
9734cf49a43SJulian Elischer 	 */
9744cf49a43SJulian Elischer 	if ((error = ng_connect(hook, hook2)))
9754cf49a43SJulian Elischer 		ng_rmnode(node2);
9764cf49a43SJulian Elischer 	return (error);
9774cf49a43SJulian Elischer }
9784cf49a43SJulian Elischer 
9794cf49a43SJulian Elischer /*
9804cf49a43SJulian Elischer  * Connect two nodes using the specified hooks
9814cf49a43SJulian Elischer  */
9824cf49a43SJulian Elischer int
9834cf49a43SJulian Elischer ng_con_nodes(node_p node, const char *name, node_p node2, const char *name2)
9844cf49a43SJulian Elischer {
9854cf49a43SJulian Elischer 	int     error;
9864cf49a43SJulian Elischer 	hook_p  hook;
9874cf49a43SJulian Elischer 	hook_p  hook2;
9884cf49a43SJulian Elischer 
9894cf49a43SJulian Elischer 	if ((error = ng_add_hook(node, name, &hook)))
9904cf49a43SJulian Elischer 		return (error);
9914cf49a43SJulian Elischer 	if ((error = ng_add_hook(node2, name2, &hook2))) {
9924cf49a43SJulian Elischer 		ng_destroy_hook(hook);
9934cf49a43SJulian Elischer 		return (error);
9944cf49a43SJulian Elischer 	}
9954cf49a43SJulian Elischer 	return (ng_connect(hook, hook2));
9964cf49a43SJulian Elischer }
997069154d5SJulian Elischer /************************************************************************
998069154d5SJulian Elischer 		Utility routines to send self messages
999069154d5SJulian Elischer ************************************************************************/
1000069154d5SJulian Elischer /*
1001069154d5SJulian Elischer  * Static version of shutdown message. we don't want to need resources
1002069154d5SJulian Elischer  * to shut down (we may be doing it to release resources because we ran out.
1003069154d5SJulian Elischer  */
1004069154d5SJulian Elischer static struct	ng_mesg  ng_msg_shutdown = {
1005069154d5SJulian Elischer 	{NG_VERSION,		/* u_char */
1006069154d5SJulian Elischer 	0,			/* u_char spare */
1007069154d5SJulian Elischer 	0,			/* u_int16_t arglen */
1008069154d5SJulian Elischer 	NGF_STATIC,		/* u_int32_t flags */
1009069154d5SJulian Elischer 	0,			/* u_int32_t token */
1010069154d5SJulian Elischer 	NGM_GENERIC_COOKIE,	/* u_int32_t */
1011069154d5SJulian Elischer 	NGM_SHUTDOWN,		/* u_int32_t */
1012069154d5SJulian Elischer 	"shutdown"}		/* u_char[16] */
1013069154d5SJulian Elischer };
1014069154d5SJulian Elischer 
1015069154d5SJulian Elischer int
1016069154d5SJulian Elischer ng_rmnode_self(node_p here)
1017069154d5SJulian Elischer {
1018069154d5SJulian Elischer 	item_p	item;
1019069154d5SJulian Elischer 	struct	ng_mesg	*msg;
10204cf49a43SJulian Elischer 
10214cf49a43SJulian Elischer 	/*
1022069154d5SJulian Elischer 	 * Use the static version to avoid needing
1023069154d5SJulian Elischer 	 * memory allocation to succeed.
1024069154d5SJulian Elischer 	 * The message is never written to and always the same.
1025069154d5SJulian Elischer 	 */
1026069154d5SJulian Elischer 	msg = &ng_msg_shutdown;
1027069154d5SJulian Elischer 
1028069154d5SJulian Elischer 	/*
1029069154d5SJulian Elischer 	 * Try get a queue item to send it with.
1030069154d5SJulian Elischer 	 * Hopefully since it has a reserve, we can get one.
1031069154d5SJulian Elischer 	 * If we can't we are screwed anyhow.
1032069154d5SJulian Elischer 	 * Increase the chances by flushing our queue first.
1033069154d5SJulian Elischer 	 * We may free an item, (if we were the hog).
1034069154d5SJulian Elischer 	 * Work in progress is allowed to complete.
1035069154d5SJulian Elischer 	 * We also pretty much ensure that we come straight
1036069154d5SJulian Elischer 	 * back in to do the shutdown. It may be a good idea
1037069154d5SJulian Elischer 	 * to hold a reference actually to stop it from all
1038069154d5SJulian Elischer 	 * going up in smoke.
1039069154d5SJulian Elischer 	 */
1040069154d5SJulian Elischer /*	ng_flush_input_queue(&here->input_queue); will mask problem  */
1041069154d5SJulian Elischer 	item = ng_package_msg_self(here, NULL, msg);
1042069154d5SJulian Elischer 	if (item == NULL) { /* it would have freed the msg except static */
1043069154d5SJulian Elischer 		/* try again after flushing our queue */
1044069154d5SJulian Elischer 		ng_flush_input_queue(&here->input_queue);
1045069154d5SJulian Elischer 		item = ng_package_msg_self(here, NULL, msg);
1046069154d5SJulian Elischer 		if (item == NULL) {
1047069154d5SJulian Elischer 			printf("failed to free node 0x%x\n", ng_node2ID(here));
1048069154d5SJulian Elischer 			return (ENOMEM);
1049069154d5SJulian Elischer 		}
1050069154d5SJulian Elischer 	}
1051069154d5SJulian Elischer 	return (ng_snd_item(item, 0));
1052069154d5SJulian Elischer }
1053069154d5SJulian Elischer 
1054069154d5SJulian Elischer /***********************************************************************
10554cf49a43SJulian Elischer  * Parse and verify a string of the form:  <NODE:><PATH>
10564cf49a43SJulian Elischer  *
10574cf49a43SJulian Elischer  * Such a string can refer to a specific node or a specific hook
10584cf49a43SJulian Elischer  * on a specific node, depending on how you look at it. In the
10594cf49a43SJulian Elischer  * latter case, the PATH component must not end in a dot.
10604cf49a43SJulian Elischer  *
10614cf49a43SJulian Elischer  * Both <NODE:> and <PATH> are optional. The <PATH> is a string
10624cf49a43SJulian Elischer  * of hook names separated by dots. This breaks out the original
10634cf49a43SJulian Elischer  * string, setting *nodep to "NODE" (or NULL if none) and *pathp
10644cf49a43SJulian Elischer  * to "PATH" (or NULL if degenerate). Also, *hookp will point to
10654cf49a43SJulian Elischer  * the final hook component of <PATH>, if any, otherwise NULL.
10664cf49a43SJulian Elischer  *
10674cf49a43SJulian Elischer  * This returns -1 if the path is malformed. The char ** are optional.
1068069154d5SJulian Elischer  ***********************************************************************/
10694cf49a43SJulian Elischer int
10704cf49a43SJulian Elischer ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp)
10714cf49a43SJulian Elischer {
10724cf49a43SJulian Elischer 	char   *node, *path, *hook;
10734cf49a43SJulian Elischer 	int     k;
10744cf49a43SJulian Elischer 
10754cf49a43SJulian Elischer 	/*
10764cf49a43SJulian Elischer 	 * Extract absolute NODE, if any
10774cf49a43SJulian Elischer 	 */
10784cf49a43SJulian Elischer 	for (path = addr; *path && *path != ':'; path++);
10794cf49a43SJulian Elischer 	if (*path) {
10804cf49a43SJulian Elischer 		node = addr;	/* Here's the NODE */
10814cf49a43SJulian Elischer 		*path++ = '\0';	/* Here's the PATH */
10824cf49a43SJulian Elischer 
10834cf49a43SJulian Elischer 		/* Node name must not be empty */
10844cf49a43SJulian Elischer 		if (!*node)
10854cf49a43SJulian Elischer 			return -1;
10864cf49a43SJulian Elischer 
10874cf49a43SJulian Elischer 		/* A name of "." is OK; otherwise '.' not allowed */
10884cf49a43SJulian Elischer 		if (strcmp(node, ".") != 0) {
10894cf49a43SJulian Elischer 			for (k = 0; node[k]; k++)
10904cf49a43SJulian Elischer 				if (node[k] == '.')
10914cf49a43SJulian Elischer 					return -1;
10924cf49a43SJulian Elischer 		}
10934cf49a43SJulian Elischer 	} else {
10944cf49a43SJulian Elischer 		node = NULL;	/* No absolute NODE */
10954cf49a43SJulian Elischer 		path = addr;	/* Here's the PATH */
10964cf49a43SJulian Elischer 	}
10974cf49a43SJulian Elischer 
10984cf49a43SJulian Elischer 	/* Snoop for illegal characters in PATH */
10994cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
11004cf49a43SJulian Elischer 		if (path[k] == ':')
11014cf49a43SJulian Elischer 			return -1;
11024cf49a43SJulian Elischer 
11034cf49a43SJulian Elischer 	/* Check for no repeated dots in PATH */
11044cf49a43SJulian Elischer 	for (k = 0; path[k]; k++)
11054cf49a43SJulian Elischer 		if (path[k] == '.' && path[k + 1] == '.')
11064cf49a43SJulian Elischer 			return -1;
11074cf49a43SJulian Elischer 
11084cf49a43SJulian Elischer 	/* Remove extra (degenerate) dots from beginning or end of PATH */
11094cf49a43SJulian Elischer 	if (path[0] == '.')
11104cf49a43SJulian Elischer 		path++;
11114cf49a43SJulian Elischer 	if (*path && path[strlen(path) - 1] == '.')
11124cf49a43SJulian Elischer 		path[strlen(path) - 1] = 0;
11134cf49a43SJulian Elischer 
11144cf49a43SJulian Elischer 	/* If PATH has a dot, then we're not talking about a hook */
11154cf49a43SJulian Elischer 	if (*path) {
11164cf49a43SJulian Elischer 		for (hook = path, k = 0; path[k]; k++)
11174cf49a43SJulian Elischer 			if (path[k] == '.') {
11184cf49a43SJulian Elischer 				hook = NULL;
11194cf49a43SJulian Elischer 				break;
11204cf49a43SJulian Elischer 			}
11214cf49a43SJulian Elischer 	} else
11224cf49a43SJulian Elischer 		path = hook = NULL;
11234cf49a43SJulian Elischer 
11244cf49a43SJulian Elischer 	/* Done */
11254cf49a43SJulian Elischer 	if (nodep)
11264cf49a43SJulian Elischer 		*nodep = node;
11274cf49a43SJulian Elischer 	if (pathp)
11284cf49a43SJulian Elischer 		*pathp = path;
11294cf49a43SJulian Elischer 	if (hookp)
11304cf49a43SJulian Elischer 		*hookp = hook;
11314cf49a43SJulian Elischer 	return (0);
11324cf49a43SJulian Elischer }
11334cf49a43SJulian Elischer 
11344cf49a43SJulian Elischer /*
11354cf49a43SJulian Elischer  * Given a path, which may be absolute or relative, and a starting node,
1136069154d5SJulian Elischer  * return the destination node.
11374cf49a43SJulian Elischer  */
11384cf49a43SJulian Elischer int
1139069154d5SJulian Elischer ng_path2noderef(node_p here, const char *address,
1140069154d5SJulian Elischer 				node_p *destp, hook_p *lasthook)
11414cf49a43SJulian Elischer {
11424cf49a43SJulian Elischer 	char    fullpath[NG_PATHLEN + 1];
11434cf49a43SJulian Elischer 	char   *nodename, *path, pbuf[2];
1144069154d5SJulian Elischer 	node_p  node, oldnode;
11454cf49a43SJulian Elischer 	char   *cp;
1146a4ec03cfSJulian Elischer 	hook_p hook = NULL;
11474cf49a43SJulian Elischer 
11484cf49a43SJulian Elischer 	/* Initialize */
11494cf49a43SJulian Elischer 	if (destp == NULL)
11504cf49a43SJulian Elischer 		return EINVAL;
11514cf49a43SJulian Elischer 	*destp = NULL;
11524cf49a43SJulian Elischer 
11534cf49a43SJulian Elischer 	/* Make a writable copy of address for ng_path_parse() */
11544cf49a43SJulian Elischer 	strncpy(fullpath, address, sizeof(fullpath) - 1);
11554cf49a43SJulian Elischer 	fullpath[sizeof(fullpath) - 1] = '\0';
11564cf49a43SJulian Elischer 
11574cf49a43SJulian Elischer 	/* Parse out node and sequence of hooks */
11584cf49a43SJulian Elischer 	if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) {
11594cf49a43SJulian Elischer 		TRAP_ERROR;
11604cf49a43SJulian Elischer 		return EINVAL;
11614cf49a43SJulian Elischer 	}
11624cf49a43SJulian Elischer 	if (path == NULL) {
11634cf49a43SJulian Elischer 		pbuf[0] = '.';	/* Needs to be writable */
11644cf49a43SJulian Elischer 		pbuf[1] = '\0';
11654cf49a43SJulian Elischer 		path = pbuf;
11664cf49a43SJulian Elischer 	}
11674cf49a43SJulian Elischer 
1168069154d5SJulian Elischer 	/*
1169069154d5SJulian Elischer 	 * For an absolute address, jump to the starting node.
1170069154d5SJulian Elischer 	 * Note that this holds a reference on the node for us.
1171069154d5SJulian Elischer 	 * Don't forget to drop the reference if we don't need it.
1172069154d5SJulian Elischer 	 */
11734cf49a43SJulian Elischer 	if (nodename) {
1174069154d5SJulian Elischer 		node = ng_name2noderef(here, nodename);
11754cf49a43SJulian Elischer 		if (node == NULL) {
11764cf49a43SJulian Elischer 			TRAP_ERROR;
11774cf49a43SJulian Elischer 			return (ENOENT);
11784cf49a43SJulian Elischer 		}
1179069154d5SJulian Elischer 	} else {
1180069154d5SJulian Elischer 		if (here == NULL) {
1181069154d5SJulian Elischer 			TRAP_ERROR
1182069154d5SJulian Elischer 			return (EINVAL);
1183069154d5SJulian Elischer 		}
11844cf49a43SJulian Elischer 		node = here;
1185069154d5SJulian Elischer 		node->refs++;
1186069154d5SJulian Elischer 	}
11874cf49a43SJulian Elischer 
1188069154d5SJulian Elischer 	/*
1189069154d5SJulian Elischer 	 * Now follow the sequence of hooks
1190069154d5SJulian Elischer 	 * XXX
1191069154d5SJulian Elischer 	 * We actually cannot guarantee that the sequence
1192069154d5SJulian Elischer 	 * is not being demolished as we crawl along it
1193069154d5SJulian Elischer 	 * without extra-ordinary locking etc.
1194069154d5SJulian Elischer 	 * So this is a bit dodgy to say the least.
1195069154d5SJulian Elischer 	 * We can probably hold up some things by holding
1196069154d5SJulian Elischer 	 * the nodelist mutex for the time of this
1197069154d5SJulian Elischer 	 * crawl if we wanted.. At least that way we wouldn't have to
1198069154d5SJulian Elischer 	 * worry about the nodes dissappearing, but the hooks would still
1199069154d5SJulian Elischer 	 * be a problem.
1200069154d5SJulian Elischer 	 */
12014cf49a43SJulian Elischer 	for (cp = path; node != NULL && *cp != '\0'; ) {
12024cf49a43SJulian Elischer 		char *segment;
12034cf49a43SJulian Elischer 
12044cf49a43SJulian Elischer 		/*
12054cf49a43SJulian Elischer 		 * Break out the next path segment. Replace the dot we just
12064cf49a43SJulian Elischer 		 * found with a NUL; "cp" points to the next segment (or the
12074cf49a43SJulian Elischer 		 * NUL at the end).
12084cf49a43SJulian Elischer 		 */
12094cf49a43SJulian Elischer 		for (segment = cp; *cp != '\0'; cp++) {
12104cf49a43SJulian Elischer 			if (*cp == '.') {
12114cf49a43SJulian Elischer 				*cp++ = '\0';
12124cf49a43SJulian Elischer 				break;
12134cf49a43SJulian Elischer 			}
12144cf49a43SJulian Elischer 		}
12154cf49a43SJulian Elischer 
12164cf49a43SJulian Elischer 		/* Empty segment */
12174cf49a43SJulian Elischer 		if (*segment == '\0')
12184cf49a43SJulian Elischer 			continue;
12194cf49a43SJulian Elischer 
12204cf49a43SJulian Elischer 		/* We have a segment, so look for a hook by that name */
1221899e9c4eSArchie Cobbs 		hook = ng_findhook(node, segment);
12224cf49a43SJulian Elischer 
12234cf49a43SJulian Elischer 		/* Can't get there from here... */
12244cf49a43SJulian Elischer 		if (hook == NULL
12254cf49a43SJulian Elischer 		    || hook->peer == NULL
1226069154d5SJulian Elischer 		    || (hook->flags & HK_INVALID) != 0
1227069154d5SJulian Elischer 		    || (hook->peer->flags & HK_INVALID) != 0) {
12284cf49a43SJulian Elischer 			TRAP_ERROR;
1229069154d5SJulian Elischer 			ng_unref(node);
12304cf49a43SJulian Elischer 			return (ENOENT);
12314cf49a43SJulian Elischer 		}
12324cf49a43SJulian Elischer 
1233069154d5SJulian Elischer 		/*
1234069154d5SJulian Elischer 		 * Hop on over to the next node
1235069154d5SJulian Elischer 		 * XXX
1236069154d5SJulian Elischer 		 * Big race conditions here as hooks and nodes go away
1237069154d5SJulian Elischer 		 * *** Idea.. store an ng_ID_t in each hook and use that
1238069154d5SJulian Elischer 		 * instead of the direct hook in this crawl?
1239069154d5SJulian Elischer 		 */
1240069154d5SJulian Elischer 		oldnode = node;
1241069154d5SJulian Elischer 		if ((node = hook->peer->node))
1242069154d5SJulian Elischer 			node->refs++;	/* XXX RACE */
1243069154d5SJulian Elischer 		ng_unref(oldnode);	/* XXX another race */
1244069154d5SJulian Elischer 		if (node->flags & NG_INVALID) {
1245069154d5SJulian Elischer 			ng_unref(node);	/* XXX more races */
1246069154d5SJulian Elischer 			node = NULL;
1247069154d5SJulian Elischer 		}
12484cf49a43SJulian Elischer 	}
12494cf49a43SJulian Elischer 
12504cf49a43SJulian Elischer 	/* If node somehow missing, fail here (probably this is not needed) */
12514cf49a43SJulian Elischer 	if (node == NULL) {
12524cf49a43SJulian Elischer 		TRAP_ERROR;
12534cf49a43SJulian Elischer 		return (ENXIO);
12544cf49a43SJulian Elischer 	}
12554cf49a43SJulian Elischer 
12564cf49a43SJulian Elischer 	/* Done */
12574cf49a43SJulian Elischer 	*destp = node;
12581bdebe4dSArchie Cobbs 	if (lasthook != NULL)
1259069154d5SJulian Elischer 		*lasthook = (hook ? hook->peer : NULL);
1260069154d5SJulian Elischer 	return (0);
1261069154d5SJulian Elischer }
1262069154d5SJulian Elischer 
1263069154d5SJulian Elischer /***************************************************************\
1264069154d5SJulian Elischer * Input queue handling.
1265069154d5SJulian Elischer * All activities are submitted to the node via the input queue
1266069154d5SJulian Elischer * which implements a multiple-reader/single-writer gate.
1267069154d5SJulian Elischer * Items which cannot be handled immeditly are queued.
1268069154d5SJulian Elischer *
1269069154d5SJulian Elischer * read-write queue locking inline functions			*
1270069154d5SJulian Elischer \***************************************************************/
1271069154d5SJulian Elischer 
1272069154d5SJulian Elischer static __inline item_p ng_dequeue(struct ng_queue * ngq);
1273069154d5SJulian Elischer static __inline item_p ng_acquire_read(struct ng_queue * ngq,
1274069154d5SJulian Elischer 					item_p  item);
1275069154d5SJulian Elischer static __inline item_p ng_acquire_write(struct ng_queue * ngq,
1276069154d5SJulian Elischer 					item_p  item);
1277069154d5SJulian Elischer static __inline void	ng_leave_read(struct ng_queue * ngq);
1278069154d5SJulian Elischer static __inline void	ng_leave_write(struct ng_queue * ngq);
1279069154d5SJulian Elischer static __inline void	ng_queue_rw(struct ng_queue * ngq,
1280069154d5SJulian Elischer 					item_p  item, int rw);
1281069154d5SJulian Elischer 
1282069154d5SJulian Elischer /*
1283069154d5SJulian Elischer  * Definition of the bits fields in the ng_queue flag word.
1284069154d5SJulian Elischer  * Defined here rather than in netgraph.h because no-one should fiddle
1285069154d5SJulian Elischer  * with them.
1286069154d5SJulian Elischer  *
1287069154d5SJulian Elischer  * The ordering here is important! don't shuffle these. If you add
1288069154d5SJulian Elischer  * READ_PENDING to the word when it has READ_PENDING already set, you
1289069154d5SJulian Elischer  * generate a carry into the reader count, this you atomically add a reader,
1290069154d5SJulian Elischer  * and remove the pending reader count! Similarly for the pending writer
1291069154d5SJulian Elischer  * flag, adding WRITE_PENDING generates a carry and sets the WRITER_ACTIVE
1292069154d5SJulian Elischer  * flag, while clearing WRITE_PENDING. When 'SINGLE_THREAD_ONLY' is set, then
1293069154d5SJulian Elischer  * it is only permitted to do WRITER operations. Reader operations will
1294069154d5SJulian Elischer  * result in errors.
1295069154d5SJulian Elischer  * But that "hack" is unnecessary: "cpp" can do the math for us!
1296069154d5SJulian Elischer  */
1297069154d5SJulian Elischer /*-
1298069154d5SJulian Elischer  Safety Barrier--------+ (adjustable to suit taste) (not used yet)
1299069154d5SJulian Elischer                        |
1300069154d5SJulian Elischer                        V
1301069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
1302069154d5SJulian Elischer | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
1303069154d5SJulian Elischer |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |R|A|W|S|
1304069154d5SJulian Elischer | | | | | | | | | | | | | | | | | | | | | | | | | | | | |P|W|P|T|
1305069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+
1306069154d5SJulian Elischer \_________________________ ____________________________/ | | | |
1307069154d5SJulian Elischer                           V                              | | | |
1308069154d5SJulian Elischer                 [active reader count]                    | | | |
1309069154d5SJulian Elischer                                                          | | | |
1310069154d5SJulian Elischer         Read Pending ------------------------------------+ | | |
1311069154d5SJulian Elischer                                                            | | |
1312069154d5SJulian Elischer         Active Writer -------------------------------------+ | |
1313069154d5SJulian Elischer                                                              | |
1314069154d5SJulian Elischer         Write Pending ---------------------------------------+ |
1315069154d5SJulian Elischer                                                                |
1316069154d5SJulian Elischer         Single Threading Only ---------------------------------+
1317069154d5SJulian Elischer */
1318069154d5SJulian Elischer #define	SINGLE_THREAD_ONLY 0x00000001	/* if set, even reads single thread */
1319069154d5SJulian Elischer #define WRITE_PENDING	0x00000002
1320069154d5SJulian Elischer #define	WRITER_ACTIVE	0x00000004
1321069154d5SJulian Elischer #define READ_PENDING	0x00000008
1322069154d5SJulian Elischer #define	READER_INCREMENT 0x00000010
1323069154d5SJulian Elischer #define	READER_MASK	0xfffffff0	/* Not valid if WRITER_ACTIVE is set */
1324069154d5SJulian Elischer #define SAFETY_BARRIER	0x00100000	/* 64K items queued should be enough */
1325069154d5SJulian Elischer /*
1326069154d5SJulian Elischer  * Taking into account the current state of the queue and node, possibly take
1327069154d5SJulian Elischer  * the next entry off the queue and return it. Return NULL if there was
1328069154d5SJulian Elischer  * nothing we could return, either because there really was nothing there, or
1329069154d5SJulian Elischer  * because the node was in a state where it cannot yet process the next item
1330069154d5SJulian Elischer  * on the queue.
1331069154d5SJulian Elischer  *
1332069154d5SJulian Elischer  * This MUST MUST MUST be called with the mutex held.
1333069154d5SJulian Elischer  */
1334069154d5SJulian Elischer static __inline item_p
1335069154d5SJulian Elischer ng_dequeue(struct ng_queue *ngq)
1336069154d5SJulian Elischer {
1337069154d5SJulian Elischer 	item_p item;
1338069154d5SJulian Elischer 	u_int		add_arg;
1339069154d5SJulian Elischer 	/*
1340069154d5SJulian Elischer 	 * If there is a writer, then the answer is "no". Everything else
1341069154d5SJulian Elischer 	 * stops when there is a WRITER.
1342069154d5SJulian Elischer 	 */
1343069154d5SJulian Elischer 	if (ngq->q_flags & WRITER_ACTIVE) {
1344069154d5SJulian Elischer 		return (NULL);
1345069154d5SJulian Elischer 	}
1346069154d5SJulian Elischer 	/* Now take a look at what's on the queue and what's running */
1347069154d5SJulian Elischer 	if ((ngq->q_flags & ~(READER_MASK | SINGLE_THREAD_ONLY)) == READ_PENDING) {
1348069154d5SJulian Elischer 		/*
1349069154d5SJulian Elischer 		 * It was a reader and we have no write active. We don't care
1350069154d5SJulian Elischer 		 * how many readers are already active. Adjust the count for
1351069154d5SJulian Elischer 		 * the item we are about to dequeue. Adding READ_PENDING to
1352069154d5SJulian Elischer 		 * the exisiting READ_PENDING clears it and generates a carry
1353069154d5SJulian Elischer 		 * into the reader count.
1354069154d5SJulian Elischer 		 */
1355069154d5SJulian Elischer 		add_arg = READ_PENDING;
1356069154d5SJulian Elischer 	} else if ((ngq->q_flags & ~SINGLE_THREAD_ONLY) == WRITE_PENDING) {
1357069154d5SJulian Elischer 		/*
1358069154d5SJulian Elischer 		 * There is a pending write, no readers and no active writer.
1359069154d5SJulian Elischer 		 * This means we can go ahead with the pending writer. Note
1360069154d5SJulian Elischer 		 * the fact that we now have a writer, ready for when we take
1361069154d5SJulian Elischer 		 * it off the queue.
1362069154d5SJulian Elischer 		 *
1363069154d5SJulian Elischer 		 * We don't need to worry about a possible collision with the
1364069154d5SJulian Elischer 		 * fasttrack reader.
1365069154d5SJulian Elischer 		 *
1366069154d5SJulian Elischer 		 * The fasttrack thread may take a long time to discover that we
1367069154d5SJulian Elischer 		 * are running so we would have an inconsistent state in the
1368069154d5SJulian Elischer 		 * flags for a while. Since we ignore the reader count
1369069154d5SJulian Elischer 		 * entirely when the WRITER_ACTIVE flag is set, this should
1370069154d5SJulian Elischer 		 * not matter (in fact it is defined that way). If it tests
1371069154d5SJulian Elischer 		 * the flag before this operation, the WRITE_PENDING flag
1372069154d5SJulian Elischer 		 * will make it fail, and if it tests it later, the
1373069154d5SJulian Elischer 		 * ACTIVE_WRITER flag will do the same. If it is SO slow that
1374069154d5SJulian Elischer 		 * we have actually completed the operation, and neither flag
1375069154d5SJulian Elischer 		 * is set (nor the READ_PENDING) by the time that it tests
1376069154d5SJulian Elischer 		 * the flags, then it is actually ok for it to continue. If
1377069154d5SJulian Elischer 		 * it completes and we've finished and the read pending is
1378069154d5SJulian Elischer 		 * set it still fails.
1379069154d5SJulian Elischer 		 *
1380069154d5SJulian Elischer 		 * So we can just ignore it,  as long as we can ensure that the
1381069154d5SJulian Elischer 		 * transition from WRITE_PENDING state to the WRITER_ACTIVE
1382069154d5SJulian Elischer 		 * state is atomic.
1383069154d5SJulian Elischer 		 *
1384069154d5SJulian Elischer 		 * After failing, first it will be held back by the mutex, then
1385069154d5SJulian Elischer 		 * when it can proceed, it will queue its request, then it
1386069154d5SJulian Elischer 		 * would arrive at this function. Usually it will have to
1387069154d5SJulian Elischer 		 * leave empty handed because the ACTIVE WRITER bit wil be
1388069154d5SJulian Elischer 		 * set.
1389069154d5SJulian Elischer 		 */
1390069154d5SJulian Elischer 		/*
1391069154d5SJulian Elischer 		 * Adjust the flags for the item we are about to dequeue.
1392069154d5SJulian Elischer 		 * Adding WRITE_PENDING to the exisiting WRITE_PENDING clears
1393069154d5SJulian Elischer 		 * it and generates a carry into the WRITER_ACTIVE flag, all
1394069154d5SJulian Elischer 		 * atomically.
1395069154d5SJulian Elischer 		 */
1396069154d5SJulian Elischer 		add_arg = WRITE_PENDING;
1397069154d5SJulian Elischer 		/*
1398069154d5SJulian Elischer 		 * We want to write "active writer, no readers " Now go make
1399069154d5SJulian Elischer 		 * it true. In fact there may be a number in the readers
1400069154d5SJulian Elischer 		 * count but we know it is not true and will be fixed soon.
1401069154d5SJulian Elischer 		 * We will fix the flags for the next pending entry in a
1402069154d5SJulian Elischer 		 * moment.
1403069154d5SJulian Elischer 		 */
1404069154d5SJulian Elischer 	} else {
1405069154d5SJulian Elischer 		/*
1406069154d5SJulian Elischer 		 * We can't dequeue anything.. return and say so. Probably we
1407069154d5SJulian Elischer 		 * have a write pending and the readers count is non zero. If
1408069154d5SJulian Elischer 		 * we got here because a reader hit us just at the wrong
1409069154d5SJulian Elischer 		 * moment with the fasttrack code, and put us in a strange
1410069154d5SJulian Elischer 		 * state, then it will be through in just a moment, (as soon
1411069154d5SJulian Elischer 		 * as we release the mutex) and keep things moving.
1412069154d5SJulian Elischer 		 */
14134cf49a43SJulian Elischer 		return (0);
14144cf49a43SJulian Elischer 	}
14154cf49a43SJulian Elischer 
14164cf49a43SJulian Elischer 	/*
1417069154d5SJulian Elischer 	 * Now we dequeue the request (whatever it may be) and correct the
1418069154d5SJulian Elischer 	 * pending flags and the next and last pointers.
14194cf49a43SJulian Elischer 	 */
1420069154d5SJulian Elischer 	item = ngq->queue;
1421069154d5SJulian Elischer 	ngq->queue = item->el_next;
1422069154d5SJulian Elischer 	if (ngq->last == &(item->el_next)) {
14234cf49a43SJulian Elischer 		/*
1424069154d5SJulian Elischer 		 * that was the last entry in the queue so set the 'last
1425069154d5SJulian Elischer 		 * pointer up correctly and make sure the pending flags are
1426069154d5SJulian Elischer 		 * clear.
14274cf49a43SJulian Elischer 		 */
1428069154d5SJulian Elischer 		ngq->last = &(ngq->queue);
1429859a4d16SJulian Elischer 		/*
1430069154d5SJulian Elischer 		 * Whatever flag was set is cleared and the carry sets the
1431069154d5SJulian Elischer 		 * correct new active state/count. So we don't need to change
1432069154d5SJulian Elischer 		 * add_arg.
1433859a4d16SJulian Elischer 		 */
1434859a4d16SJulian Elischer 	} else {
1435069154d5SJulian Elischer 		if ((ngq->queue->el_flags & NGQF_TYPE) == NGQF_READER) {
1436069154d5SJulian Elischer 			/*
1437069154d5SJulian Elischer 			 * If we had a READ_PENDING and have another one, we
1438069154d5SJulian Elischer 			 * just want to add READ_PENDING twice (the same as
1439069154d5SJulian Elischer 			 * adding READER_INCREMENT). If we had WRITE_PENDING,
1440069154d5SJulian Elischer 			 * we want to add READ_PENDING + WRITE_PENDING to
1441069154d5SJulian Elischer 			 * clear the old WRITE_PENDING, set ACTIVE_WRITER,
1442069154d5SJulian Elischer 			 * and set READ_PENDING. Either way we just add
1443069154d5SJulian Elischer 			 * READ_PENDING to whatever we already had.
1444069154d5SJulian Elischer 			 */
1445069154d5SJulian Elischer 			add_arg += READ_PENDING;
1446069154d5SJulian Elischer 		} else {
1447069154d5SJulian Elischer 			/*
1448069154d5SJulian Elischer 			 * If we had a WRITE_PENDING and have another one, we
1449069154d5SJulian Elischer 			 * just want to add WRITE_PENDING twice (the same as
1450069154d5SJulian Elischer 			 * adding ACTIVE_WRITER). If we had READ_PENDING, we
1451069154d5SJulian Elischer 			 * want to add READ_PENDING + WRITE_PENDING to clear
1452069154d5SJulian Elischer 			 * the old READ_PENDING, increment the readers, and
1453069154d5SJulian Elischer 			 * set WRITE_PENDING. Either way we just add
1454069154d5SJulian Elischer 			 * WRITE_PENDING to whatever we already had.
1455069154d5SJulian Elischer 			 */
1456069154d5SJulian Elischer 			add_arg += WRITE_PENDING;
1457859a4d16SJulian Elischer 		}
1458859a4d16SJulian Elischer 	}
1459069154d5SJulian Elischer 	atomic_add_long(&ngq->q_flags, add_arg);
1460069154d5SJulian Elischer 	/*
1461069154d5SJulian Elischer 	 * We have successfully cleared the old pending flag, set the new one
1462069154d5SJulian Elischer 	 * if it is needed, and incremented the appropriate active field.
1463069154d5SJulian Elischer 	 * (all in one atomic addition.. wow)
1464069154d5SJulian Elischer 	 */
1465069154d5SJulian Elischer 	return (item);
1466069154d5SJulian Elischer }
1467859a4d16SJulian Elischer 
1468859a4d16SJulian Elischer /*
1469069154d5SJulian Elischer  * Queue a packet to be picked up by someone else.
1470069154d5SJulian Elischer  * We really don't care who, but we can't or don't want to hang around
1471069154d5SJulian Elischer  * to process it ourselves. We are probably an interrupt routine..
1472069154d5SJulian Elischer  * 1 = writer, 0 = reader
1473069154d5SJulian Elischer  * We should set something to indicate NETISR requested
1474069154d5SJulian Elischer  * If it's the first item queued.
1475859a4d16SJulian Elischer  */
1476069154d5SJulian Elischer #define NGQRW_R 0
1477069154d5SJulian Elischer #define NGQRW_W 1
1478069154d5SJulian Elischer static __inline void
1479069154d5SJulian Elischer ng_queue_rw(struct ng_queue * ngq, item_p  item, int rw)
1480069154d5SJulian Elischer {
1481069154d5SJulian Elischer 	item->el_next = NULL;	/* maybe not needed */
1482069154d5SJulian Elischer 	*ngq->last = item;
1483069154d5SJulian Elischer 	/*
1484069154d5SJulian Elischer 	 * If it was the first item in the queue then we need to
1485069154d5SJulian Elischer 	 * set the last pointer and the type flags.
1486069154d5SJulian Elischer 	 */
1487069154d5SJulian Elischer 	if (ngq->last == &(ngq->queue)) {
1488069154d5SJulian Elischer 		/*
1489069154d5SJulian Elischer 		 * When called with constants for rw, the optimiser will
1490069154d5SJulian Elischer 		 * remove the unneeded branch below.
1491069154d5SJulian Elischer 		 */
1492069154d5SJulian Elischer 		if (rw == NGQRW_W) {
1493069154d5SJulian Elischer 			atomic_add_long(&ngq->q_flags, WRITE_PENDING);
1494069154d5SJulian Elischer 		} else {
1495069154d5SJulian Elischer 			atomic_add_long(&ngq->q_flags, READ_PENDING);
1496069154d5SJulian Elischer 		}
1497069154d5SJulian Elischer 	}
1498069154d5SJulian Elischer 	ngq->last = &(item->el_next);
1499069154d5SJulian Elischer }
1500069154d5SJulian Elischer 
1501069154d5SJulian Elischer 
1502069154d5SJulian Elischer /*
1503069154d5SJulian Elischer  * This function 'cheats' in that it first tries to 'grab' the use of the
1504069154d5SJulian Elischer  * node, without going through the mutex. We can do this becasue of the
1505069154d5SJulian Elischer  * semantics of the lock. The semantics include a clause that says that the
1506069154d5SJulian Elischer  * value of the readers count is invalid if the WRITER_ACTIVE flag is set. It
1507069154d5SJulian Elischer  * also says that the WRITER_ACTIVE flag cannot be set if the readers count
1508069154d5SJulian Elischer  * is not zero. Note that this talks about what is valid to SET the
1509069154d5SJulian Elischer  * WRITER_ACTIVE flag, because from the moment it is set, the value if the
1510069154d5SJulian Elischer  * reader count is immaterial, and not valid. The two 'pending' flags have a
1511069154d5SJulian Elischer  * similar effect, in that If they are orthogonal to the two active fields in
1512069154d5SJulian Elischer  * how they are set, but if either is set, the attempted 'grab' need to be
1513069154d5SJulian Elischer  * backed out because there is earlier work, and we maintain ordering in the
1514069154d5SJulian Elischer  * queue. The result of this is that the reader request can try obtain use of
1515069154d5SJulian Elischer  * the node with only a single atomic addition, and without any of the mutex
1516069154d5SJulian Elischer  * overhead. If this fails the operation degenerates to the same as for other
1517069154d5SJulian Elischer  * cases.
1518069154d5SJulian Elischer  *
1519069154d5SJulian Elischer  */
1520069154d5SJulian Elischer static __inline item_p
1521069154d5SJulian Elischer ng_acquire_read(struct ng_queue *ngq, item_p item)
1522069154d5SJulian Elischer {
1523069154d5SJulian Elischer 
1524069154d5SJulian Elischer 	/* ######### Hack alert ######### */
1525069154d5SJulian Elischer 	atomic_add_long(&ngq->q_flags, READER_INCREMENT);
1526069154d5SJulian Elischer 	if ((ngq->q_flags & (~READER_MASK)) == 0) {
1527069154d5SJulian Elischer 		/* Successfully grabbed node */
1528069154d5SJulian Elischer 		return (item);
1529069154d5SJulian Elischer 	}
1530069154d5SJulian Elischer 	/* undo the damage if we didn't succeed */
1531069154d5SJulian Elischer 	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
1532069154d5SJulian Elischer 
1533069154d5SJulian Elischer 	/* ######### End Hack alert ######### */
1534069154d5SJulian Elischer 	mtx_enter((&ngq->q_mtx), MTX_SPIN);
1535069154d5SJulian Elischer 	/*
1536069154d5SJulian Elischer 	 * Try again. Another processor (or interrupt for that matter) may
1537069154d5SJulian Elischer 	 * have removed the last queued item that was stopping us from
1538069154d5SJulian Elischer 	 * running, between the previous test, and the moment that we took
1539069154d5SJulian Elischer 	 * the mutex. (Or maybe a writer completed.)
1540069154d5SJulian Elischer 	 */
1541069154d5SJulian Elischer 	if ((ngq->q_flags & (~READER_MASK)) == 0) {
1542069154d5SJulian Elischer 		atomic_add_long(&ngq->q_flags, READER_INCREMENT);
1543069154d5SJulian Elischer 		mtx_exit((&ngq->q_mtx), MTX_SPIN);
1544069154d5SJulian Elischer 		return (item);
1545069154d5SJulian Elischer 	}
1546069154d5SJulian Elischer 
1547069154d5SJulian Elischer 	/*
1548069154d5SJulian Elischer 	 * Quick check that we are doing things right.
1549069154d5SJulian Elischer 	 */
1550069154d5SJulian Elischer 	if (ngq->q_flags & SINGLE_THREAD_ONLY) {
1551069154d5SJulian Elischer 		panic("Calling single treaded queue incorrectly");
1552069154d5SJulian Elischer 	}
1553069154d5SJulian Elischer 
1554069154d5SJulian Elischer 	/*
1555069154d5SJulian Elischer 	 * and queue the request for later.
1556069154d5SJulian Elischer 	 */
1557069154d5SJulian Elischer 	item->el_flags |= NGQF_TYPE;
1558069154d5SJulian Elischer 	ng_queue_rw(ngq, item, NGQRW_R);
1559069154d5SJulian Elischer 
1560069154d5SJulian Elischer 	/*
1561069154d5SJulian Elischer 	 * Ok, so that's the item successfully queued for later. So now we
1562069154d5SJulian Elischer 	 * see if we can dequeue something to run instead.
1563069154d5SJulian Elischer 	 */
1564069154d5SJulian Elischer 	item = ng_dequeue(ngq);
1565069154d5SJulian Elischer 	mtx_exit(&(ngq->q_mtx), MTX_SPIN);
1566069154d5SJulian Elischer 	return (item);
1567069154d5SJulian Elischer }
1568069154d5SJulian Elischer 
1569069154d5SJulian Elischer static __inline item_p
1570069154d5SJulian Elischer ng_acquire_write(struct ng_queue *ngq, item_p item)
1571069154d5SJulian Elischer {
1572069154d5SJulian Elischer restart:
1573069154d5SJulian Elischer 	mtx_enter(&(ngq->q_mtx), MTX_SPIN);
1574069154d5SJulian Elischer 	/*
1575069154d5SJulian Elischer 	 * If there are no readers, no writer, and no pending packets, then
1576069154d5SJulian Elischer 	 * we can just go ahead. In all other situations we need to queue the
1577069154d5SJulian Elischer 	 * request
1578069154d5SJulian Elischer 	 */
1579069154d5SJulian Elischer 	if ((ngq->q_flags & (~SINGLE_THREAD_ONLY)) == 0) {
1580069154d5SJulian Elischer 		atomic_add_long(&ngq->q_flags, WRITER_ACTIVE);
1581069154d5SJulian Elischer 		mtx_exit((&ngq->q_mtx), MTX_SPIN);
1582069154d5SJulian Elischer 		if (ngq->q_flags & READER_MASK) {
1583069154d5SJulian Elischer 			/* Collision with fast-track reader */
1584069154d5SJulian Elischer 			atomic_add_long(&ngq->q_flags, -WRITER_ACTIVE);
1585069154d5SJulian Elischer 			goto restart;
1586069154d5SJulian Elischer 		}
1587069154d5SJulian Elischer 
1588069154d5SJulian Elischer 		return (item);
1589069154d5SJulian Elischer 	}
1590069154d5SJulian Elischer 
1591069154d5SJulian Elischer 	/*
1592069154d5SJulian Elischer 	 * and queue the request for later.
1593069154d5SJulian Elischer 	 */
1594069154d5SJulian Elischer 	item->el_flags &= ~NGQF_TYPE;
1595069154d5SJulian Elischer 	ng_queue_rw(ngq, item, NGQRW_W);
1596069154d5SJulian Elischer 
1597069154d5SJulian Elischer 	/*
1598069154d5SJulian Elischer 	 * Ok, so that's the item successfully queued for later. So now we
1599069154d5SJulian Elischer 	 * see if we can dequeue something to run instead.
1600069154d5SJulian Elischer 	 */
1601069154d5SJulian Elischer 	item = ng_dequeue(ngq);
1602069154d5SJulian Elischer 	mtx_exit(&(ngq->q_mtx), MTX_SPIN);
1603069154d5SJulian Elischer 	return (item);
1604069154d5SJulian Elischer }
1605069154d5SJulian Elischer 
1606069154d5SJulian Elischer static __inline void
1607069154d5SJulian Elischer ng_leave_read(struct ng_queue *ngq)
1608069154d5SJulian Elischer {
1609069154d5SJulian Elischer 	atomic_subtract_long(&ngq->q_flags, READER_INCREMENT);
1610069154d5SJulian Elischer }
1611069154d5SJulian Elischer 
1612069154d5SJulian Elischer static __inline void
1613069154d5SJulian Elischer ng_leave_write(struct ng_queue *ngq)
1614069154d5SJulian Elischer {
1615069154d5SJulian Elischer 	atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE);
1616069154d5SJulian Elischer }
1617069154d5SJulian Elischer 
1618069154d5SJulian Elischer static void
1619069154d5SJulian Elischer ng_flush_input_queue(struct ng_queue * ngq)
1620069154d5SJulian Elischer {
1621069154d5SJulian Elischer 	item_p item;
1622069154d5SJulian Elischer 	u_int		add_arg;
1623069154d5SJulian Elischer 	mtx_enter(&ngq->q_mtx, MTX_SPIN);
1624069154d5SJulian Elischer 	for (;;) {
1625069154d5SJulian Elischer 		/* Now take a look at what's on the queue */
1626069154d5SJulian Elischer 		if (ngq->q_flags & READ_PENDING) {
1627069154d5SJulian Elischer 			add_arg = -READ_PENDING;
1628069154d5SJulian Elischer 		} else if (ngq->q_flags & WRITE_PENDING) {
1629069154d5SJulian Elischer 			add_arg = -WRITE_PENDING;
1630069154d5SJulian Elischer 		} else {
1631069154d5SJulian Elischer 			break;
1632069154d5SJulian Elischer 		}
1633069154d5SJulian Elischer 
1634069154d5SJulian Elischer 		item = ngq->queue;
1635069154d5SJulian Elischer 		ngq->queue = item->el_next;
1636069154d5SJulian Elischer 		if (ngq->last == &(item->el_next)) {
1637069154d5SJulian Elischer 			ngq->last = &(ngq->queue);
1638069154d5SJulian Elischer 		} else {
1639069154d5SJulian Elischer 			if ((ngq->queue->el_flags & NGQF_TYPE) == NGQF_READER) {
1640069154d5SJulian Elischer 				add_arg += READ_PENDING;
1641069154d5SJulian Elischer 			} else {
1642069154d5SJulian Elischer 				add_arg += WRITE_PENDING;
1643069154d5SJulian Elischer 			}
1644069154d5SJulian Elischer 		}
1645069154d5SJulian Elischer 		atomic_add_long(&ngq->q_flags, add_arg);
1646069154d5SJulian Elischer 
1647069154d5SJulian Elischer 		mtx_exit(&ngq->q_mtx, MTX_SPIN);
1648069154d5SJulian Elischer 		NG_FREE_ITEM(item);
1649069154d5SJulian Elischer 		mtx_enter(&ngq->q_mtx, MTX_SPIN);
1650069154d5SJulian Elischer 	}
1651069154d5SJulian Elischer 	mtx_exit(&ngq->q_mtx, MTX_SPIN);
1652069154d5SJulian Elischer }
1653069154d5SJulian Elischer 
1654069154d5SJulian Elischer /***********************************************************************
1655069154d5SJulian Elischer * Externally visible method for sending or queueing messages or data.
1656069154d5SJulian Elischer ***********************************************************************/
1657069154d5SJulian Elischer 
1658069154d5SJulian Elischer /*
1659069154d5SJulian Elischer  * MACRO WILL DO THE JOB OF CALLING ng_package_msg IN CALLER
1660069154d5SJulian Elischer  * before we are called. The user code should have filled out the item
1661069154d5SJulian Elischer  * correctly by this stage:
1662069154d5SJulian Elischer  * Common:
1663069154d5SJulian Elischer  *    reference to destination node.
1664069154d5SJulian Elischer  *    Reference to destination rcv hook if relevant.
1665069154d5SJulian Elischer  * Data:
1666069154d5SJulian Elischer  *    pointer to mbuf
1667069154d5SJulian Elischer  *    pointer to metadata
1668069154d5SJulian Elischer  * Control_Message:
1669069154d5SJulian Elischer  *    pointer to msg.
1670069154d5SJulian Elischer  *    ID of original sender node. (return address)
1671069154d5SJulian Elischer  *
1672069154d5SJulian Elischer  * The nodes have several routines and macros to help with this task:
1673069154d5SJulian Elischer  * ng_package_msg()
1674069154d5SJulian Elischer  * ng_package_data() do much of the work.
1675069154d5SJulian Elischer  * ng_retarget_msg
1676069154d5SJulian Elischer  * ng_retarget_data
1677069154d5SJulian Elischer  */
1678069154d5SJulian Elischer 
1679069154d5SJulian Elischer int
1680069154d5SJulian Elischer ng_snd_item(item_p item, int queue)
1681069154d5SJulian Elischer {
1682069154d5SJulian Elischer 	hook_p hook = item->el_hook;
1683069154d5SJulian Elischer 	node_p dest = item->el_dest;
1684069154d5SJulian Elischer 	int rw;
1685069154d5SJulian Elischer 	int error = 0, ierror;
1686069154d5SJulian Elischer 	item_p	oitem;
1687069154d5SJulian Elischer 	struct ng_queue * ngq = &dest->input_queue;
1688069154d5SJulian Elischer 
1689069154d5SJulian Elischer #ifdef	ITEM_DEBUG
1690069154d5SJulian Elischer         _ngi_check(item, __FILE__, __LINE__);
1691069154d5SJulian Elischer #endif
1692069154d5SJulian Elischer 
1693069154d5SJulian Elischer 	if (item == NULL) {
1694069154d5SJulian Elischer 		return (EINVAL);	/* failed to get queue element */
1695069154d5SJulian Elischer 	}
1696069154d5SJulian Elischer 	if (dest == NULL) {
1697069154d5SJulian Elischer 		NG_FREE_ITEM(item);
1698069154d5SJulian Elischer 		return (EINVAL);	/* No address */
1699069154d5SJulian Elischer 	}
1700069154d5SJulian Elischer 	if ((item->el_flags & NGQF_D_M) == NGQF_DATA) {
1701069154d5SJulian Elischer 		/*
1702069154d5SJulian Elischer 		 * DATA MESSAGE
1703069154d5SJulian Elischer 		 * Delivered to a node via a non-optional hook.
1704069154d5SJulian Elischer 		 * Both should be present in the item even though
1705069154d5SJulian Elischer 		 * the node is derivable from the hook.
1706069154d5SJulian Elischer 		 * References are held on both by the item.
1707069154d5SJulian Elischer 		 */
1708069154d5SJulian Elischer #ifdef	ITEM_DEBUG
1709069154d5SJulian Elischer         _ngi_check(item, __FILE__, __LINE__);
1710069154d5SJulian Elischer #endif
1711069154d5SJulian Elischer 		CHECK_DATA_MBUF(NGI_M(item));
1712069154d5SJulian Elischer 		if (hook == NULL) {
1713069154d5SJulian Elischer 			NG_FREE_ITEM(item);
1714069154d5SJulian Elischer 			return(EINVAL);
1715069154d5SJulian Elischer 		}
1716069154d5SJulian Elischer 		if (((hook->flags & HK_INVALID) != 0)
1717069154d5SJulian Elischer 		|| ((hook->node->flags & NG_INVALID) != 0)) {
1718859a4d16SJulian Elischer 			TRAP_ERROR;
1719069154d5SJulian Elischer 			NG_FREE_ITEM(item);
1720069154d5SJulian Elischer 			return (ENOTCONN);
1721859a4d16SJulian Elischer 		}
1722069154d5SJulian Elischer 		if ((hook->flags & HK_QUEUE)) {
1723069154d5SJulian Elischer 			queue = 1;
17244cf49a43SJulian Elischer 		}
1725069154d5SJulian Elischer 		/* By default data is a reader in the locking scheme */
1726069154d5SJulian Elischer 		item->el_flags |= NGQF_READER;
1727069154d5SJulian Elischer 		rw = NGQRW_R;
1728069154d5SJulian Elischer 	} else {
17294cf49a43SJulian Elischer 		/*
1730069154d5SJulian Elischer 		 * CONTROL MESSAGE
1731069154d5SJulian Elischer 		 * Delivered to a node.
1732069154d5SJulian Elischer 		 * Hook is optional.
1733069154d5SJulian Elischer 		 * References are held by the item on the node and
1734069154d5SJulian Elischer 		 * the hook if it is present.
17354cf49a43SJulian Elischer 		 */
1736069154d5SJulian Elischer 		if (hook && (hook->flags & HK_QUEUE)) {
1737069154d5SJulian Elischer 			queue = 1;
1738069154d5SJulian Elischer 		}
1739069154d5SJulian Elischer 		/* Data messages count as writers unles explicitly exempted */
1740069154d5SJulian Elischer 		if (NGI_MSG(item)->header.cmd & NGM_READONLY) {
1741069154d5SJulian Elischer 			item->el_flags |= NGQF_READER;
1742069154d5SJulian Elischer 			rw = NGQRW_R;
1743069154d5SJulian Elischer 		} else {
1744069154d5SJulian Elischer 			item->el_flags &= ~NGQF_TYPE;
1745069154d5SJulian Elischer 			rw = NGQRW_W;
1746069154d5SJulian Elischer 		}
1747069154d5SJulian Elischer 	}
1748069154d5SJulian Elischer 	/*
1749069154d5SJulian Elischer 	 * If the node specifies single threading, force writer semantics
1750069154d5SJulian Elischer 	 * Similarly the node may say one hook always produces writers.
1751069154d5SJulian Elischer 	 * These are over-rides.
1752069154d5SJulian Elischer 	 */
1753069154d5SJulian Elischer 	if ((ngq->q_flags & SINGLE_THREAD_ONLY)
1754069154d5SJulian Elischer 	|| (dest->flags & NG_FORCE_WRITER)
1755069154d5SJulian Elischer 	|| (hook && (hook->flags & HK_FORCE_WRITER))) {
1756069154d5SJulian Elischer 			rw = NGQRW_W;
1757069154d5SJulian Elischer 			item->el_flags &= ~NGQF_TYPE;
1758069154d5SJulian Elischer 	}
1759069154d5SJulian Elischer 	if (queue) {
1760069154d5SJulian Elischer 		/* Put it on the queue for that node*/
1761069154d5SJulian Elischer #ifdef	ITEM_DEBUG
1762069154d5SJulian Elischer         _ngi_check(item, __FILE__, __LINE__);
1763069154d5SJulian Elischer #endif
1764069154d5SJulian Elischer 		mtx_enter(&(ngq->q_mtx), MTX_SPIN);
1765069154d5SJulian Elischer 		ng_queue_rw(ngq, item, rw);
1766069154d5SJulian Elischer 		mtx_exit(&(ngq->q_mtx), MTX_SPIN);
1767069154d5SJulian Elischer 		/*
1768069154d5SJulian Elischer 		 * If there are active elements then we can rely on
1769069154d5SJulian Elischer 		 * them. if not we should not rely on another packet
1770069154d5SJulian Elischer 		 * coming here by another path,
1771069154d5SJulian Elischer 		 * so it is best to put us in the netisr list.
1772069154d5SJulian Elischer 		 */
1773069154d5SJulian Elischer 		if ((ngq->q_flags & (READER_MASK|WRITER_ACTIVE)) == 0) {
1774069154d5SJulian Elischer 			ng_setisr(ngq->q_node);
1775069154d5SJulian Elischer 		}
1776069154d5SJulian Elischer 		return (0);
1777069154d5SJulian Elischer 	}
1778069154d5SJulian Elischer 	/*
1779069154d5SJulian Elischer 	 * Take a queue item and a node and see if we can apply the item to
1780069154d5SJulian Elischer 	 * the node. We may end up getting a different item to apply instead.
1781069154d5SJulian Elischer 	 * Will allow for a piggyback reply only in the case where
1782069154d5SJulian Elischer 	 * there is no queueing.
1783069154d5SJulian Elischer 	 */
1784069154d5SJulian Elischer 
1785069154d5SJulian Elischer 	oitem = item;
1786069154d5SJulian Elischer 	/*
1787069154d5SJulian Elischer 	 * We already decided how we will be queueud or treated.
1788069154d5SJulian Elischer 	 * Try get the appropriate operating permission.
1789069154d5SJulian Elischer 	 */
1790069154d5SJulian Elischer  	if (rw == NGQRW_R) {
1791069154d5SJulian Elischer 		item = ng_acquire_read(ngq, item);
1792069154d5SJulian Elischer 	} else {
1793069154d5SJulian Elischer 		item = ng_acquire_write(ngq, item);
17944cf49a43SJulian Elischer 	}
17954cf49a43SJulian Elischer 
17964cf49a43SJulian Elischer 	/*
1797069154d5SJulian Elischer 	 * May have come back with a different item.
1798069154d5SJulian Elischer 	 * or maybe none at all. The one we started with will
1799069154d5SJulian Elischer 	 * have been queued in thises cases.
1800069154d5SJulian Elischer 	 */
1801069154d5SJulian Elischer 	if (item == NULL) {
1802069154d5SJulian Elischer 		return (0);
1803069154d5SJulian Elischer 	}
1804069154d5SJulian Elischer 
1805069154d5SJulian Elischer #ifdef	ITEM_DEBUG
1806069154d5SJulian Elischer         _ngi_check(item, __FILE__, __LINE__);
1807069154d5SJulian Elischer #endif
1808069154d5SJulian Elischer 	ierror = ng_apply_item(dest, item); /* drops r/w lock when done */
1809069154d5SJulian Elischer 
1810069154d5SJulian Elischer 	/* only return an error if it was our initial item.. (compat hack) */
1811069154d5SJulian Elischer 	if (oitem == item) {
1812069154d5SJulian Elischer 		error = ierror;
1813069154d5SJulian Elischer 	}
1814069154d5SJulian Elischer 
1815069154d5SJulian Elischer 	/*
1816069154d5SJulian Elischer 	 * Now we've handled the packet we brought, (or a friend of it) let's
1817069154d5SJulian Elischer 	 * look for any other packets that may have been queued up. We hold
1818069154d5SJulian Elischer 	 * no locks, so if someone puts something in the queue after
1819069154d5SJulian Elischer 	 * we check that it is empty, it is their problem
1820069154d5SJulian Elischer 	 * to ensure it is processed. If we have the netisr thread cme in here
1821069154d5SJulian Elischer 	 * while we still say we have stuff to do, we may get a boost
1822069154d5SJulian Elischer 	 * in SMP systems. :-)
1823069154d5SJulian Elischer 	 */
1824069154d5SJulian Elischer 	for (;;) {
1825069154d5SJulian Elischer 		/* quick hack to save all that mutex stuff */
1826069154d5SJulian Elischer 		if ((ngq->q_flags & (WRITE_PENDING | READ_PENDING)) == 0) {
1827069154d5SJulian Elischer 			if (dest->flags & NG_WORKQ)
1828069154d5SJulian Elischer 				ng_worklist_remove(dest);
1829069154d5SJulian Elischer 			return (0);
1830069154d5SJulian Elischer 		}
1831069154d5SJulian Elischer 		/*
1832069154d5SJulian Elischer 		 * dequeue acquires and adjusts the input_queue as it dequeues
1833069154d5SJulian Elischer 		 * packets. It acquires the rw lock as needed.
1834069154d5SJulian Elischer 		 */
1835069154d5SJulian Elischer 		mtx_enter(&ngq->q_mtx, MTX_SPIN);
1836069154d5SJulian Elischer 		item = ng_dequeue(ngq);
1837069154d5SJulian Elischer 		mtx_exit(&ngq->q_mtx, MTX_SPIN);
1838069154d5SJulian Elischer 		if (!item) {
1839069154d5SJulian Elischer 			/*
1840069154d5SJulian Elischer 			 * If we have no work to do
1841069154d5SJulian Elischer 			 * then we certainly don't need to be
1842069154d5SJulian Elischer 			 * on the worklist.
1843069154d5SJulian Elischer 			 */
1844069154d5SJulian Elischer 			if (dest->flags & NG_WORKQ)
1845069154d5SJulian Elischer 				ng_worklist_remove(dest);
1846069154d5SJulian Elischer 			return (0);
1847069154d5SJulian Elischer 		}
1848069154d5SJulian Elischer #ifdef	ITEM_DEBUG
1849069154d5SJulian Elischer         _ngi_check(item, __FILE__, __LINE__);
1850069154d5SJulian Elischer #endif
1851069154d5SJulian Elischer 
1852069154d5SJulian Elischer 		/*
1853069154d5SJulian Elischer 		 * We have the appropriate lock, so run the item.
1854069154d5SJulian Elischer 		 * When finished it will drop the lock accordingly
1855069154d5SJulian Elischer 		 */
1856069154d5SJulian Elischer 
1857069154d5SJulian Elischer 		ierror = ng_apply_item(dest, item);
1858069154d5SJulian Elischer 		/*
1859069154d5SJulian Elischer 		 * only return an error if it was our initial
1860069154d5SJulian Elischer 		 * item.. (compat hack)
1861069154d5SJulian Elischer 		 */
1862069154d5SJulian Elischer 		if (oitem == item) {
1863069154d5SJulian Elischer 			error = ierror;
1864069154d5SJulian Elischer 		}
1865069154d5SJulian Elischer 	}
1866069154d5SJulian Elischer 	return (0);
1867069154d5SJulian Elischer }
1868069154d5SJulian Elischer 
1869069154d5SJulian Elischer /*
1870069154d5SJulian Elischer  * We have an item that was possibly queued somewhere.
1871069154d5SJulian Elischer  * It should contain all the information needed
1872069154d5SJulian Elischer  * to run it on the appropriate node/hook.
18734cf49a43SJulian Elischer  */
18744cf49a43SJulian Elischer static int
1875069154d5SJulian Elischer ng_apply_item(node_p node, item_p item)
1876069154d5SJulian Elischer {
1877069154d5SJulian Elischer 	hook_p  hook;
1878069154d5SJulian Elischer 	int was_reader = ((item->el_flags & NGQF_TYPE));
1879069154d5SJulian Elischer 	int error = 0;
1880069154d5SJulian Elischer 	ng_rcvdata_t *rcvdata;
1881069154d5SJulian Elischer 
1882069154d5SJulian Elischer 	hook = item->el_hook;
1883069154d5SJulian Elischer 	item->el_hook = NULL;	/* so NG_FREE_ITEM doesn't ng_unref_hook() */
1884069154d5SJulian Elischer 	/* We already have the node.. assume responsibility */
1885069154d5SJulian Elischer 	/* And the reference */
1886069154d5SJulian Elischer 	/* node = item->el_dest; */
1887069154d5SJulian Elischer 	item->el_dest = NULL;	/* same as for the hook above */
1888069154d5SJulian Elischer #ifdef	ITEM_DEBUG
1889069154d5SJulian Elischer         _ngi_check(item, __FILE__, __LINE__);
1890069154d5SJulian Elischer #endif
1891069154d5SJulian Elischer 
1892069154d5SJulian Elischer 	switch (item->el_flags & NGQF_D_M) {
1893069154d5SJulian Elischer 	case NGQF_DATA:
1894069154d5SJulian Elischer 		/*
1895069154d5SJulian Elischer 		 * Check things are still ok as when we were queued.
1896069154d5SJulian Elischer 		 */
1897069154d5SJulian Elischer 
1898069154d5SJulian Elischer 		if ((hook == NULL)
1899069154d5SJulian Elischer 		|| ((hook->flags & HK_INVALID) != 0)
1900069154d5SJulian Elischer 		|| ((hook->node->flags & NG_INVALID) != 0)
1901069154d5SJulian Elischer 		|| ((rcvdata = hook->node->type->rcvdata) == NULL)) {
1902069154d5SJulian Elischer 			error = EIO;
1903069154d5SJulian Elischer 			NG_FREE_ITEM(item);
1904069154d5SJulian Elischer 		} else {
1905069154d5SJulian Elischer 			error = (*rcvdata)(hook, item);
1906069154d5SJulian Elischer 		}
1907069154d5SJulian Elischer 		break;
1908069154d5SJulian Elischer 	case NGQF_MESG:
1909069154d5SJulian Elischer 
1910069154d5SJulian Elischer 		if (hook) {
1911069154d5SJulian Elischer 			item->el_hook = NULL;
1912069154d5SJulian Elischer 			if ((hook->flags & HK_INVALID) != 0) {
1913069154d5SJulian Elischer 			/*
1914069154d5SJulian Elischer 			 * If the hook has been zapped then we can't use it.
1915069154d5SJulian Elischer 			 * Immediatly drop its reference.
1916069154d5SJulian Elischer 			 * The message may not need it.
1917069154d5SJulian Elischer 			 */
1918069154d5SJulian Elischer 				ng_unref_hook(hook);
1919069154d5SJulian Elischer 				hook = NULL;
1920069154d5SJulian Elischer 			}
1921069154d5SJulian Elischer 		}
1922069154d5SJulian Elischer 		/*
1923069154d5SJulian Elischer 		 * Similarly, if the node is a zombie there is
1924069154d5SJulian Elischer 		 * nothing we can do with it, drop everything.
1925069154d5SJulian Elischer 		 */
1926069154d5SJulian Elischer 		if (node->flags & NG_INVALID) {
1927069154d5SJulian Elischer 			error = EINVAL;
1928069154d5SJulian Elischer 			NG_FREE_ITEM(item);
1929069154d5SJulian Elischer 		} else {
1930069154d5SJulian Elischer 			/*
1931069154d5SJulian Elischer 			 * Call the appropriate message handler for the object.
1932069154d5SJulian Elischer 			 * It is up to the message handler to free the message.
1933069154d5SJulian Elischer 			 * If it's a generic message, handle it generically,
1934069154d5SJulian Elischer 			 * otherwise call the type's message handler
1935069154d5SJulian Elischer 			 * (if it exists)
1936069154d5SJulian Elischer 			 * XXX (race). Remember that a queued message may
1937069154d5SJulian Elischer 			 * reference a node or hook that has just been
1938069154d5SJulian Elischer 			 * invalidated. It will exist as the queue code
1939069154d5SJulian Elischer 			 * is holding a reference, but..
1940069154d5SJulian Elischer 			 */
1941069154d5SJulian Elischer 
1942069154d5SJulian Elischer 			struct ng_mesg *msg = NGI_MSG(item);
1943069154d5SJulian Elischer 
1944069154d5SJulian Elischer 			if ((msg->header.typecookie == NGM_GENERIC_COOKIE)
1945069154d5SJulian Elischer 			&& ((msg->header.flags & NGF_RESP) == 0)) {
1946069154d5SJulian Elischer 				error = ng_generic_msg(node, item, hook);
1947069154d5SJulian Elischer 			} else {
1948069154d5SJulian Elischer 				if ((node)->type->rcvmsg != NULL) {
1949069154d5SJulian Elischer 					error = (*(node)->type->rcvmsg)((node),
1950069154d5SJulian Elischer 						(item), (hook));
1951069154d5SJulian Elischer 				} else {
1952069154d5SJulian Elischer 					TRAP_ERROR;
1953069154d5SJulian Elischer 					error = EINVAL; /* XXX */
1954069154d5SJulian Elischer 					NG_FREE_ITEM(item);
1955069154d5SJulian Elischer 				}
1956069154d5SJulian Elischer 			}
1957069154d5SJulian Elischer 			/* item is now invalid */
1958069154d5SJulian Elischer 		}
1959069154d5SJulian Elischer 		break;
1960069154d5SJulian Elischer 	}
1961069154d5SJulian Elischer 	/*
1962069154d5SJulian Elischer 	 * We held references on some of the resources
1963069154d5SJulian Elischer 	 * that we took from the item. Now that we have
1964069154d5SJulian Elischer 	 * finished doing everything, drop those references.
1965069154d5SJulian Elischer 	 */
1966069154d5SJulian Elischer 	if (hook) {
1967069154d5SJulian Elischer 		ng_unref_hook(hook);
1968069154d5SJulian Elischer 	}
1969069154d5SJulian Elischer 
1970069154d5SJulian Elischer 	if (was_reader) {
1971069154d5SJulian Elischer 		ng_leave_read(&node->input_queue);
1972069154d5SJulian Elischer 	} else {
1973069154d5SJulian Elischer 		ng_leave_write(&node->input_queue);
1974069154d5SJulian Elischer 	}
1975069154d5SJulian Elischer 	ng_unref(node);
1976069154d5SJulian Elischer 	return (error);
1977069154d5SJulian Elischer }
1978069154d5SJulian Elischer 
1979069154d5SJulian Elischer /***********************************************************************
1980069154d5SJulian Elischer  * Implement the 'generic' control messages
1981069154d5SJulian Elischer  ***********************************************************************/
1982069154d5SJulian Elischer static int
1983069154d5SJulian Elischer ng_generic_msg(node_p here, item_p item, hook_p lasthook)
19844cf49a43SJulian Elischer {
19854cf49a43SJulian Elischer 	int error = 0;
1986069154d5SJulian Elischer 	struct ng_mesg *msg;
1987069154d5SJulian Elischer 	struct ng_mesg *resp = NULL;
19884cf49a43SJulian Elischer 
1989069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
19904cf49a43SJulian Elischer 	if (msg->header.typecookie != NGM_GENERIC_COOKIE) {
1991069154d5SJulian Elischer 		error = EINVAL;
1992069154d5SJulian Elischer 		goto out;
19934cf49a43SJulian Elischer 	}
19944cf49a43SJulian Elischer 	switch (msg->header.cmd) {
19954cf49a43SJulian Elischer 	case NGM_SHUTDOWN:
19964cf49a43SJulian Elischer 		ng_rmnode(here);
19974cf49a43SJulian Elischer 		break;
19984cf49a43SJulian Elischer 	case NGM_MKPEER:
19994cf49a43SJulian Elischer 	    {
20004cf49a43SJulian Elischer 		struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data;
20014cf49a43SJulian Elischer 
20024cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*mkp)) {
2003069154d5SJulian Elischer 			error = EINVAL;
2004069154d5SJulian Elischer 			break;
20054cf49a43SJulian Elischer 		}
20064cf49a43SJulian Elischer 		mkp->type[sizeof(mkp->type) - 1] = '\0';
20074cf49a43SJulian Elischer 		mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0';
20084cf49a43SJulian Elischer 		mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0';
20094cf49a43SJulian Elischer 		error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type);
20104cf49a43SJulian Elischer 		break;
20114cf49a43SJulian Elischer 	    }
20124cf49a43SJulian Elischer 	case NGM_CONNECT:
20134cf49a43SJulian Elischer 	    {
20144cf49a43SJulian Elischer 		struct ngm_connect *const con =
20154cf49a43SJulian Elischer 			(struct ngm_connect *) msg->data;
20164cf49a43SJulian Elischer 		node_p node2;
20174cf49a43SJulian Elischer 
20184cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*con)) {
2019069154d5SJulian Elischer 			error = EINVAL;
2020069154d5SJulian Elischer 			break;
20214cf49a43SJulian Elischer 		}
20224cf49a43SJulian Elischer 		con->path[sizeof(con->path) - 1] = '\0';
20234cf49a43SJulian Elischer 		con->ourhook[sizeof(con->ourhook) - 1] = '\0';
20244cf49a43SJulian Elischer 		con->peerhook[sizeof(con->peerhook) - 1] = '\0';
2025069154d5SJulian Elischer 		/* Don't forget we get a reference.. */
2026069154d5SJulian Elischer 		error = ng_path2noderef(here, con->path, &node2, NULL);
20274cf49a43SJulian Elischer 		if (error)
20284cf49a43SJulian Elischer 			break;
20294cf49a43SJulian Elischer 		error = ng_con_nodes(here, con->ourhook, node2, con->peerhook);
2030069154d5SJulian Elischer 		ng_unref(node2);
20314cf49a43SJulian Elischer 		break;
20324cf49a43SJulian Elischer 	    }
20334cf49a43SJulian Elischer 	case NGM_NAME:
20344cf49a43SJulian Elischer 	    {
20354cf49a43SJulian Elischer 		struct ngm_name *const nam = (struct ngm_name *) msg->data;
20364cf49a43SJulian Elischer 
20374cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*nam)) {
2038069154d5SJulian Elischer 			error = EINVAL;
2039069154d5SJulian Elischer 			break;
20404cf49a43SJulian Elischer 		}
20414cf49a43SJulian Elischer 		nam->name[sizeof(nam->name) - 1] = '\0';
20424cf49a43SJulian Elischer 		error = ng_name_node(here, nam->name);
20434cf49a43SJulian Elischer 		break;
20444cf49a43SJulian Elischer 	    }
20454cf49a43SJulian Elischer 	case NGM_RMHOOK:
20464cf49a43SJulian Elischer 	    {
20474cf49a43SJulian Elischer 		struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data;
20484cf49a43SJulian Elischer 		hook_p hook;
20494cf49a43SJulian Elischer 
20504cf49a43SJulian Elischer 		if (msg->header.arglen != sizeof(*rmh)) {
2051069154d5SJulian Elischer 			error = EINVAL;
2052069154d5SJulian Elischer 			break;
20534cf49a43SJulian Elischer 		}
20544cf49a43SJulian Elischer 		rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0';
2055899e9c4eSArchie Cobbs 		if ((hook = ng_findhook(here, rmh->ourhook)) != NULL)
20564cf49a43SJulian Elischer 			ng_destroy_hook(hook);
20574cf49a43SJulian Elischer 		break;
20584cf49a43SJulian Elischer 	    }
20594cf49a43SJulian Elischer 	case NGM_NODEINFO:
20604cf49a43SJulian Elischer 	    {
20614cf49a43SJulian Elischer 		struct nodeinfo *ni;
20624cf49a43SJulian Elischer 
2063069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT);
20644cf49a43SJulian Elischer 		if (resp == NULL) {
20654cf49a43SJulian Elischer 			error = ENOMEM;
20664cf49a43SJulian Elischer 			break;
20674cf49a43SJulian Elischer 		}
20684cf49a43SJulian Elischer 
20694cf49a43SJulian Elischer 		/* Fill in node info */
2070069154d5SJulian Elischer 		ni = (struct nodeinfo *) resp->data;
20714cf49a43SJulian Elischer 		if (here->name != NULL)
20724cf49a43SJulian Elischer 			strncpy(ni->name, here->name, NG_NODELEN);
20734cf49a43SJulian Elischer 		strncpy(ni->type, here->type->name, NG_TYPELEN);
2074dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
20754cf49a43SJulian Elischer 		ni->hooks = here->numhooks;
20764cf49a43SJulian Elischer 		break;
20774cf49a43SJulian Elischer 	    }
20784cf49a43SJulian Elischer 	case NGM_LISTHOOKS:
20794cf49a43SJulian Elischer 	    {
20804cf49a43SJulian Elischer 		const int nhooks = here->numhooks;
20814cf49a43SJulian Elischer 		struct hooklist *hl;
20824cf49a43SJulian Elischer 		struct nodeinfo *ni;
20834cf49a43SJulian Elischer 		hook_p hook;
20844cf49a43SJulian Elischer 
20854cf49a43SJulian Elischer 		/* Get response struct */
2086069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*hl)
20874cf49a43SJulian Elischer 		    + (nhooks * sizeof(struct linkinfo)), M_NOWAIT);
2088069154d5SJulian Elischer 		if (resp == NULL) {
20894cf49a43SJulian Elischer 			error = ENOMEM;
20904cf49a43SJulian Elischer 			break;
20914cf49a43SJulian Elischer 		}
2092069154d5SJulian Elischer 		hl = (struct hooklist *) resp->data;
20934cf49a43SJulian Elischer 		ni = &hl->nodeinfo;
20944cf49a43SJulian Elischer 
20954cf49a43SJulian Elischer 		/* Fill in node info */
20964cf49a43SJulian Elischer 		if (here->name)
20974cf49a43SJulian Elischer 			strncpy(ni->name, here->name, NG_NODELEN);
20984cf49a43SJulian Elischer 		strncpy(ni->type, here->type->name, NG_TYPELEN);
2099dc90cad9SJulian Elischer 		ni->id = ng_node2ID(here);
21004cf49a43SJulian Elischer 
21014cf49a43SJulian Elischer 		/* Cycle through the linked list of hooks */
21024cf49a43SJulian Elischer 		ni->hooks = 0;
21034cf49a43SJulian Elischer 		LIST_FOREACH(hook, &here->hooks, hooks) {
21044cf49a43SJulian Elischer 			struct linkinfo *const link = &hl->link[ni->hooks];
21054cf49a43SJulian Elischer 
21064cf49a43SJulian Elischer 			if (ni->hooks >= nhooks) {
21074cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
21084cf49a43SJulian Elischer 				    __FUNCTION__, "hooks");
21094cf49a43SJulian Elischer 				break;
21104cf49a43SJulian Elischer 			}
21114cf49a43SJulian Elischer 			if ((hook->flags & HK_INVALID) != 0)
21124cf49a43SJulian Elischer 				continue;
21134cf49a43SJulian Elischer 			strncpy(link->ourhook, hook->name, NG_HOOKLEN);
21144cf49a43SJulian Elischer 			strncpy(link->peerhook, hook->peer->name, NG_HOOKLEN);
2115069154d5SJulian Elischer 			if (hook->peer->node->name[0] != '\0')
21164cf49a43SJulian Elischer 				strncpy(link->nodeinfo.name,
21174cf49a43SJulian Elischer 				    hook->peer->node->name, NG_NODELEN);
21184cf49a43SJulian Elischer 			strncpy(link->nodeinfo.type,
21194cf49a43SJulian Elischer 			   hook->peer->node->type->name, NG_TYPELEN);
2120dc90cad9SJulian Elischer 			link->nodeinfo.id = ng_node2ID(hook->peer->node);
21214cf49a43SJulian Elischer 			link->nodeinfo.hooks = hook->peer->node->numhooks;
21224cf49a43SJulian Elischer 			ni->hooks++;
21234cf49a43SJulian Elischer 		}
21244cf49a43SJulian Elischer 		break;
21254cf49a43SJulian Elischer 	    }
21264cf49a43SJulian Elischer 
21274cf49a43SJulian Elischer 	case NGM_LISTNAMES:
21284cf49a43SJulian Elischer 	case NGM_LISTNODES:
21294cf49a43SJulian Elischer 	    {
21304cf49a43SJulian Elischer 		const int unnamed = (msg->header.cmd == NGM_LISTNODES);
21314cf49a43SJulian Elischer 		struct namelist *nl;
21324cf49a43SJulian Elischer 		node_p node;
21334cf49a43SJulian Elischer 		int num = 0;
21344cf49a43SJulian Elischer 
2135069154d5SJulian Elischer 		mtx_enter(&ng_nodelist_mtx, MTX_DEF);
21364cf49a43SJulian Elischer 		/* Count number of nodes */
2137069154d5SJulian Elischer 		LIST_FOREACH(node, &ng_nodelist, nodes) {
2138069154d5SJulian Elischer 			if (unnamed || node->name[0] != '\0')
21394cf49a43SJulian Elischer 				num++;
21404cf49a43SJulian Elischer 		}
2141069154d5SJulian Elischer 		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
21424cf49a43SJulian Elischer 
21434cf49a43SJulian Elischer 		/* Get response struct */
2144069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*nl)
21454cf49a43SJulian Elischer 		    + (num * sizeof(struct nodeinfo)), M_NOWAIT);
2146069154d5SJulian Elischer 		if (resp == NULL) {
21474cf49a43SJulian Elischer 			error = ENOMEM;
21484cf49a43SJulian Elischer 			break;
21494cf49a43SJulian Elischer 		}
2150069154d5SJulian Elischer 		nl = (struct namelist *) resp->data;
21514cf49a43SJulian Elischer 
21524cf49a43SJulian Elischer 		/* Cycle through the linked list of nodes */
21534cf49a43SJulian Elischer 		nl->numnames = 0;
2154069154d5SJulian Elischer 		mtx_enter(&ng_nodelist_mtx, MTX_DEF);
2155069154d5SJulian Elischer 		LIST_FOREACH(node, &ng_nodelist, nodes) {
21564cf49a43SJulian Elischer 			struct nodeinfo *const np = &nl->nodeinfo[nl->numnames];
21574cf49a43SJulian Elischer 
21584cf49a43SJulian Elischer 			if (nl->numnames >= num) {
21594cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
21604cf49a43SJulian Elischer 				    __FUNCTION__, "nodes");
21614cf49a43SJulian Elischer 				break;
21624cf49a43SJulian Elischer 			}
21634cf49a43SJulian Elischer 			if ((node->flags & NG_INVALID) != 0)
21644cf49a43SJulian Elischer 				continue;
2165069154d5SJulian Elischer 			if (!unnamed && node->name[0] == '\0')
21664cf49a43SJulian Elischer 				continue;
2167069154d5SJulian Elischer 			if (node->name[0] != '\0')
21684cf49a43SJulian Elischer 				strncpy(np->name, node->name, NG_NODELEN);
21694cf49a43SJulian Elischer 			strncpy(np->type, node->type->name, NG_TYPELEN);
2170dc90cad9SJulian Elischer 			np->id = ng_node2ID(node);
21714cf49a43SJulian Elischer 			np->hooks = node->numhooks;
21724cf49a43SJulian Elischer 			nl->numnames++;
21734cf49a43SJulian Elischer 		}
2174069154d5SJulian Elischer 		mtx_exit(&ng_nodelist_mtx, MTX_DEF);
21754cf49a43SJulian Elischer 		break;
21764cf49a43SJulian Elischer 	    }
21774cf49a43SJulian Elischer 
21784cf49a43SJulian Elischer 	case NGM_LISTTYPES:
21794cf49a43SJulian Elischer 	    {
21804cf49a43SJulian Elischer 		struct typelist *tl;
21814cf49a43SJulian Elischer 		struct ng_type *type;
21824cf49a43SJulian Elischer 		int num = 0;
21834cf49a43SJulian Elischer 
2184069154d5SJulian Elischer 		mtx_enter(&ng_typelist_mtx, MTX_DEF);
21854cf49a43SJulian Elischer 		/* Count number of types */
2186069154d5SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types)
21874cf49a43SJulian Elischer 			num++;
2188069154d5SJulian Elischer 		mtx_exit(&ng_typelist_mtx, MTX_DEF);
21894cf49a43SJulian Elischer 
21904cf49a43SJulian Elischer 		/* Get response struct */
2191069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*tl)
21924cf49a43SJulian Elischer 		    + (num * sizeof(struct typeinfo)), M_NOWAIT);
2193069154d5SJulian Elischer 		if (resp == NULL) {
21944cf49a43SJulian Elischer 			error = ENOMEM;
21954cf49a43SJulian Elischer 			break;
21964cf49a43SJulian Elischer 		}
2197069154d5SJulian Elischer 		tl = (struct typelist *) resp->data;
21984cf49a43SJulian Elischer 
21994cf49a43SJulian Elischer 		/* Cycle through the linked list of types */
22004cf49a43SJulian Elischer 		tl->numtypes = 0;
2201069154d5SJulian Elischer 		mtx_enter(&ng_typelist_mtx, MTX_DEF);
2202069154d5SJulian Elischer 		LIST_FOREACH(type, &ng_typelist, types) {
22034cf49a43SJulian Elischer 			struct typeinfo *const tp = &tl->typeinfo[tl->numtypes];
22044cf49a43SJulian Elischer 
22054cf49a43SJulian Elischer 			if (tl->numtypes >= num) {
22064cf49a43SJulian Elischer 				log(LOG_ERR, "%s: number of %s changed\n",
22074cf49a43SJulian Elischer 				    __FUNCTION__, "types");
22084cf49a43SJulian Elischer 				break;
22094cf49a43SJulian Elischer 			}
2210a096e45aSArchie Cobbs 			strncpy(tp->type_name, type->name, NG_TYPELEN);
22114cf49a43SJulian Elischer 			tp->numnodes = type->refs;
22124cf49a43SJulian Elischer 			tl->numtypes++;
22134cf49a43SJulian Elischer 		}
2214069154d5SJulian Elischer 		mtx_exit(&ng_typelist_mtx, MTX_DEF);
22154cf49a43SJulian Elischer 		break;
22164cf49a43SJulian Elischer 	    }
22174cf49a43SJulian Elischer 
2218f8307e12SArchie Cobbs 	case NGM_BINARY2ASCII:
2219f8307e12SArchie Cobbs 	    {
22207133ac27SArchie Cobbs 		int bufSize = 20 * 1024;	/* XXX hard coded constant */
2221f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2222f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2223069154d5SJulian Elischer 		struct ng_mesg *binary, *ascii;
2224f8307e12SArchie Cobbs 
2225f8307e12SArchie Cobbs 		/* Data area must contain a valid netgraph message */
2226f8307e12SArchie Cobbs 		binary = (struct ng_mesg *)msg->data;
2227f8307e12SArchie Cobbs 		if (msg->header.arglen < sizeof(struct ng_mesg)
2228f8307e12SArchie Cobbs 		    || msg->header.arglen - sizeof(struct ng_mesg)
2229f8307e12SArchie Cobbs 		      < binary->header.arglen) {
2230f8307e12SArchie Cobbs 			error = EINVAL;
2231f8307e12SArchie Cobbs 			break;
2232f8307e12SArchie Cobbs 		}
2233f8307e12SArchie Cobbs 
2234f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2235069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT);
2236069154d5SJulian Elischer 		if (resp == NULL) {
2237f8307e12SArchie Cobbs 			error = ENOMEM;
2238f8307e12SArchie Cobbs 			break;
2239f8307e12SArchie Cobbs 		}
2240069154d5SJulian Elischer 		ascii = (struct ng_mesg *)resp->data;
2241f8307e12SArchie Cobbs 
2242f8307e12SArchie Cobbs 		/* Copy binary message header to response message payload */
2243f8307e12SArchie Cobbs 		bcopy(binary, ascii, sizeof(*binary));
2244f8307e12SArchie Cobbs 
2245f8307e12SArchie Cobbs 		/* Find command by matching typecookie and command number */
2246f8307e12SArchie Cobbs 		for (c = here->type->cmdlist;
2247f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2248f8307e12SArchie Cobbs 			if (binary->header.typecookie == c->cookie
2249f8307e12SArchie Cobbs 			    && binary->header.cmd == c->cmd)
2250f8307e12SArchie Cobbs 				break;
2251f8307e12SArchie Cobbs 		}
2252f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2253f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2254f8307e12SArchie Cobbs 				if (binary->header.typecookie == c->cookie
2255f8307e12SArchie Cobbs 				    && binary->header.cmd == c->cmd)
2256f8307e12SArchie Cobbs 					break;
2257f8307e12SArchie Cobbs 			}
2258f8307e12SArchie Cobbs 			if (c->name == NULL) {
2259069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2260f8307e12SArchie Cobbs 				error = ENOSYS;
2261f8307e12SArchie Cobbs 				break;
2262f8307e12SArchie Cobbs 			}
2263f8307e12SArchie Cobbs 		}
2264f8307e12SArchie Cobbs 
2265f8307e12SArchie Cobbs 		/* Convert command name to ASCII */
2266f8307e12SArchie Cobbs 		snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr),
2267f8307e12SArchie Cobbs 		    "%s", c->name);
2268f8307e12SArchie Cobbs 
2269f8307e12SArchie Cobbs 		/* Convert command arguments to ASCII */
2270f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2271f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
2272f8307e12SArchie Cobbs 		if (argstype == NULL)
2273f8307e12SArchie Cobbs 			*ascii->data = '\0';
2274f8307e12SArchie Cobbs 		else {
2275f8307e12SArchie Cobbs 			if ((error = ng_unparse(argstype,
2276f8307e12SArchie Cobbs 			    (u_char *)binary->data,
2277f8307e12SArchie Cobbs 			    ascii->data, bufSize)) != 0) {
2278069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2279f8307e12SArchie Cobbs 				break;
2280f8307e12SArchie Cobbs 			}
2281f8307e12SArchie Cobbs 		}
2282f8307e12SArchie Cobbs 
2283f8307e12SArchie Cobbs 		/* Return the result as struct ng_mesg plus ASCII string */
2284f8307e12SArchie Cobbs 		bufSize = strlen(ascii->data) + 1;
2285f8307e12SArchie Cobbs 		ascii->header.arglen = bufSize;
2286069154d5SJulian Elischer 		resp->header.arglen = sizeof(*ascii) + bufSize;
2287f8307e12SArchie Cobbs 		break;
2288f8307e12SArchie Cobbs 	    }
2289f8307e12SArchie Cobbs 
2290f8307e12SArchie Cobbs 	case NGM_ASCII2BINARY:
2291f8307e12SArchie Cobbs 	    {
2292f8307e12SArchie Cobbs 		int bufSize = 2000;	/* XXX hard coded constant */
2293f8307e12SArchie Cobbs 		const struct ng_cmdlist *c;
2294f8307e12SArchie Cobbs 		const struct ng_parse_type *argstype;
2295069154d5SJulian Elischer 		struct ng_mesg *ascii, *binary;
229652ec4a03SArchie Cobbs 		int off = 0;
2297f8307e12SArchie Cobbs 
2298f8307e12SArchie Cobbs 		/* Data area must contain at least a struct ng_mesg + '\0' */
2299f8307e12SArchie Cobbs 		ascii = (struct ng_mesg *)msg->data;
2300f8307e12SArchie Cobbs 		if (msg->header.arglen < sizeof(*ascii) + 1
2301f8307e12SArchie Cobbs 		    || ascii->header.arglen < 1
2302f8307e12SArchie Cobbs 		    || msg->header.arglen
2303f8307e12SArchie Cobbs 		      < sizeof(*ascii) + ascii->header.arglen) {
2304f8307e12SArchie Cobbs 			error = EINVAL;
2305f8307e12SArchie Cobbs 			break;
2306f8307e12SArchie Cobbs 		}
2307f8307e12SArchie Cobbs 		ascii->data[ascii->header.arglen - 1] = '\0';
2308f8307e12SArchie Cobbs 
2309f8307e12SArchie Cobbs 		/* Get a response message with lots of room */
2310069154d5SJulian Elischer 		NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT);
2311069154d5SJulian Elischer 		if (resp == NULL) {
2312f8307e12SArchie Cobbs 			error = ENOMEM;
2313f8307e12SArchie Cobbs 			break;
2314f8307e12SArchie Cobbs 		}
2315069154d5SJulian Elischer 		binary = (struct ng_mesg *)resp->data;
2316f8307e12SArchie Cobbs 
2317f8307e12SArchie Cobbs 		/* Copy ASCII message header to response message payload */
2318f8307e12SArchie Cobbs 		bcopy(ascii, binary, sizeof(*ascii));
2319f8307e12SArchie Cobbs 
2320f8307e12SArchie Cobbs 		/* Find command by matching ASCII command string */
2321f8307e12SArchie Cobbs 		for (c = here->type->cmdlist;
2322f8307e12SArchie Cobbs 		    c != NULL && c->name != NULL; c++) {
2323f8307e12SArchie Cobbs 			if (strcmp(ascii->header.cmdstr, c->name) == 0)
2324f8307e12SArchie Cobbs 				break;
2325f8307e12SArchie Cobbs 		}
2326f8307e12SArchie Cobbs 		if (c == NULL || c->name == NULL) {
2327f8307e12SArchie Cobbs 			for (c = ng_generic_cmds; c->name != NULL; c++) {
2328f8307e12SArchie Cobbs 				if (strcmp(ascii->header.cmdstr, c->name) == 0)
2329f8307e12SArchie Cobbs 					break;
2330f8307e12SArchie Cobbs 			}
2331f8307e12SArchie Cobbs 			if (c->name == NULL) {
2332069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2333f8307e12SArchie Cobbs 				error = ENOSYS;
2334f8307e12SArchie Cobbs 				break;
2335f8307e12SArchie Cobbs 			}
2336f8307e12SArchie Cobbs 		}
2337f8307e12SArchie Cobbs 
2338f8307e12SArchie Cobbs 		/* Convert command name to binary */
2339f8307e12SArchie Cobbs 		binary->header.cmd = c->cmd;
2340f8307e12SArchie Cobbs 		binary->header.typecookie = c->cookie;
2341f8307e12SArchie Cobbs 
2342f8307e12SArchie Cobbs 		/* Convert command arguments to binary */
2343f8307e12SArchie Cobbs 		argstype = (binary->header.flags & NGF_RESP) ?
2344f8307e12SArchie Cobbs 		    c->respType : c->mesgType;
2345f8307e12SArchie Cobbs 		if (argstype == NULL)
2346f8307e12SArchie Cobbs 			bufSize = 0;
2347f8307e12SArchie Cobbs 		else {
2348f8307e12SArchie Cobbs 			if ((error = ng_parse(argstype, ascii->data,
2349f8307e12SArchie Cobbs 			    &off, (u_char *)binary->data, &bufSize)) != 0) {
2350069154d5SJulian Elischer 				NG_FREE_MSG(resp);
2351f8307e12SArchie Cobbs 				break;
2352f8307e12SArchie Cobbs 			}
2353f8307e12SArchie Cobbs 		}
2354f8307e12SArchie Cobbs 
2355f8307e12SArchie Cobbs 		/* Return the result */
2356f8307e12SArchie Cobbs 		binary->header.arglen = bufSize;
2357069154d5SJulian Elischer 		resp->header.arglen = sizeof(*binary) + bufSize;
2358f8307e12SArchie Cobbs 		break;
2359f8307e12SArchie Cobbs 	    }
2360f8307e12SArchie Cobbs 
23617095e097SPoul-Henning Kamp 	case NGM_TEXT_CONFIG:
23624cf49a43SJulian Elischer 	case NGM_TEXT_STATUS:
23634cf49a43SJulian Elischer 		/*
23644cf49a43SJulian Elischer 		 * This one is tricky as it passes the command down to the
23654cf49a43SJulian Elischer 		 * actual node, even though it is a generic type command.
2366069154d5SJulian Elischer 		 * This means we must assume that the item/msg is already freed
23674cf49a43SJulian Elischer 		 * when control passes back to us.
23684cf49a43SJulian Elischer 		 */
2369069154d5SJulian Elischer 		if (here->type->rcvmsg != NULL) {
2370069154d5SJulian Elischer 			NGI_MSG(item) = msg; /* put it back as we found it */
2371069154d5SJulian Elischer 			return((*here->type->rcvmsg)(here, item, lasthook));
23724cf49a43SJulian Elischer 		}
23734cf49a43SJulian Elischer 		/* Fall through if rcvmsg not supported */
23744cf49a43SJulian Elischer 	default:
23754cf49a43SJulian Elischer 		TRAP_ERROR;
23764cf49a43SJulian Elischer 		error = EINVAL;
23774cf49a43SJulian Elischer 	}
2378069154d5SJulian Elischer 	/*
2379069154d5SJulian Elischer 	 * Sometimes a generic message may be statically allocated
2380069154d5SJulian Elischer 	 * to avoid problems with allocating when in tight memeory situations.
2381069154d5SJulian Elischer 	 * Don't free it if it is so.
2382069154d5SJulian Elischer 	 * I break them appart here, because erros may cause a free if the item
2383069154d5SJulian Elischer 	 * in which case we'd be doing it twice.
2384069154d5SJulian Elischer 	 * they are kept together above, to simplify freeing.
2385069154d5SJulian Elischer 	 */
2386069154d5SJulian Elischer out:
2387069154d5SJulian Elischer 	NG_RESPOND_MSG(error, here, item, resp);
2388069154d5SJulian Elischer 	if ( msg && ((msg->header.flags & NGF_STATIC) == 0))
2389069154d5SJulian Elischer 		NG_FREE_MSG(msg);
23904cf49a43SJulian Elischer 	return (error);
23914cf49a43SJulian Elischer }
23924cf49a43SJulian Elischer 
23934cf49a43SJulian Elischer /*
2394a096e45aSArchie Cobbs  * Copy a 'meta'.
2395a096e45aSArchie Cobbs  *
2396a096e45aSArchie Cobbs  * Returns new meta, or NULL if original meta is NULL or ENOMEM.
2397a096e45aSArchie Cobbs  */
2398a096e45aSArchie Cobbs meta_p
2399a096e45aSArchie Cobbs ng_copy_meta(meta_p meta)
2400a096e45aSArchie Cobbs {
2401a096e45aSArchie Cobbs 	meta_p meta2;
2402a096e45aSArchie Cobbs 
2403a096e45aSArchie Cobbs 	if (meta == NULL)
2404a096e45aSArchie Cobbs 		return (NULL);
2405069154d5SJulian Elischer 	MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH_META, M_NOWAIT);
2406a096e45aSArchie Cobbs 	if (meta2 == NULL)
2407a096e45aSArchie Cobbs 		return (NULL);
2408a096e45aSArchie Cobbs 	meta2->allocated_len = meta->used_len;
2409a096e45aSArchie Cobbs 	bcopy(meta, meta2, meta->used_len);
2410a096e45aSArchie Cobbs 	return (meta2);
2411a096e45aSArchie Cobbs }
2412a096e45aSArchie Cobbs 
24134cf49a43SJulian Elischer /************************************************************************
24144cf49a43SJulian Elischer 			Module routines
24154cf49a43SJulian Elischer ************************************************************************/
24164cf49a43SJulian Elischer 
24174cf49a43SJulian Elischer /*
24184cf49a43SJulian Elischer  * Handle the loading/unloading of a netgraph node type module
24194cf49a43SJulian Elischer  */
24204cf49a43SJulian Elischer int
24214cf49a43SJulian Elischer ng_mod_event(module_t mod, int event, void *data)
24224cf49a43SJulian Elischer {
24234cf49a43SJulian Elischer 	struct ng_type *const type = data;
24244cf49a43SJulian Elischer 	int s, error = 0;
24254cf49a43SJulian Elischer 
24264cf49a43SJulian Elischer 	switch (event) {
24274cf49a43SJulian Elischer 	case MOD_LOAD:
24284cf49a43SJulian Elischer 
24294cf49a43SJulian Elischer 		/* Register new netgraph node type */
24304cf49a43SJulian Elischer 		s = splnet();
24314cf49a43SJulian Elischer 		if ((error = ng_newtype(type)) != 0) {
24324cf49a43SJulian Elischer 			splx(s);
24334cf49a43SJulian Elischer 			break;
24344cf49a43SJulian Elischer 		}
24354cf49a43SJulian Elischer 
24364cf49a43SJulian Elischer 		/* Call type specific code */
24374cf49a43SJulian Elischer 		if (type->mod_event != NULL)
2438069154d5SJulian Elischer 			if ((error = (*type->mod_event)(mod, event, data))) {
2439069154d5SJulian Elischer 				mtx_enter(&ng_typelist_mtx, MTX_DEF);
24404cf49a43SJulian Elischer 				LIST_REMOVE(type, types);
2441069154d5SJulian Elischer 				mtx_exit(&ng_typelist_mtx, MTX_DEF);
2442069154d5SJulian Elischer 			}
24434cf49a43SJulian Elischer 		splx(s);
24444cf49a43SJulian Elischer 		break;
24454cf49a43SJulian Elischer 
24464cf49a43SJulian Elischer 	case MOD_UNLOAD:
24474cf49a43SJulian Elischer 		s = splnet();
24484cf49a43SJulian Elischer 		if (type->refs != 0)		/* make sure no nodes exist! */
24494cf49a43SJulian Elischer 			error = EBUSY;
24504cf49a43SJulian Elischer 		else {
24514cf49a43SJulian Elischer 			if (type->mod_event != NULL) {	/* check with type */
24524cf49a43SJulian Elischer 				error = (*type->mod_event)(mod, event, data);
24534cf49a43SJulian Elischer 				if (error != 0) {	/* type refuses.. */
24544cf49a43SJulian Elischer 					splx(s);
24554cf49a43SJulian Elischer 					break;
24564cf49a43SJulian Elischer 				}
24574cf49a43SJulian Elischer 			}
2458069154d5SJulian Elischer 			mtx_enter(&ng_typelist_mtx, MTX_DEF);
24594cf49a43SJulian Elischer 			LIST_REMOVE(type, types);
2460069154d5SJulian Elischer 			mtx_exit(&ng_typelist_mtx, MTX_DEF);
24614cf49a43SJulian Elischer 		}
24624cf49a43SJulian Elischer 		splx(s);
24634cf49a43SJulian Elischer 		break;
24644cf49a43SJulian Elischer 
24654cf49a43SJulian Elischer 	default:
24664cf49a43SJulian Elischer 		if (type->mod_event != NULL)
24674cf49a43SJulian Elischer 			error = (*type->mod_event)(mod, event, data);
24684cf49a43SJulian Elischer 		else
24694cf49a43SJulian Elischer 			error = 0;		/* XXX ? */
24704cf49a43SJulian Elischer 		break;
24714cf49a43SJulian Elischer 	}
24724cf49a43SJulian Elischer 	return (error);
24734cf49a43SJulian Elischer }
24744cf49a43SJulian Elischer 
24754cf49a43SJulian Elischer /*
24764cf49a43SJulian Elischer  * Handle loading and unloading for this code.
24774cf49a43SJulian Elischer  * The only thing we need to link into is the NETISR strucure.
24784cf49a43SJulian Elischer  */
24794cf49a43SJulian Elischer static int
24804cf49a43SJulian Elischer ngb_mod_event(module_t mod, int event, void *data)
24814cf49a43SJulian Elischer {
24824cf49a43SJulian Elischer 	int s, error = 0;
24834cf49a43SJulian Elischer 
24844cf49a43SJulian Elischer 	switch (event) {
24854cf49a43SJulian Elischer 	case MOD_LOAD:
24864cf49a43SJulian Elischer 		/* Register line discipline */
2487069154d5SJulian Elischer 		mtx_init(&ng_worklist_mtx, "netgraph worklist mutex", 0);
2488069154d5SJulian Elischer 		mtx_init(&ng_typelist_mtx, "netgraph types mutex", 0);
2489069154d5SJulian Elischer 		mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", 0);
2490069154d5SJulian Elischer 		mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", 0);
2491069154d5SJulian Elischer 		mtx_init(&ngq_mtx, "netgraph netisr mutex", 0);
24924cf49a43SJulian Elischer 		s = splimp();
24934cf49a43SJulian Elischer 		error = register_netisr(NETISR_NETGRAPH, ngintr);
24944cf49a43SJulian Elischer 		splx(s);
24954cf49a43SJulian Elischer 		break;
24964cf49a43SJulian Elischer 	case MOD_UNLOAD:
24974cf49a43SJulian Elischer 		/* You cant unload it because an interface may be using it.  */
24984cf49a43SJulian Elischer 		error = EBUSY;
24994cf49a43SJulian Elischer 		break;
25004cf49a43SJulian Elischer 	default:
25014cf49a43SJulian Elischer 		error = EOPNOTSUPP;
25024cf49a43SJulian Elischer 		break;
25034cf49a43SJulian Elischer 	}
25044cf49a43SJulian Elischer 	return (error);
25054cf49a43SJulian Elischer }
25064cf49a43SJulian Elischer 
25074cf49a43SJulian Elischer static moduledata_t netgraph_mod = {
25084cf49a43SJulian Elischer 	"netgraph",
25094cf49a43SJulian Elischer 	ngb_mod_event,
25104cf49a43SJulian Elischer 	(NULL)
25114cf49a43SJulian Elischer };
25124cf49a43SJulian Elischer DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
25134cf49a43SJulian Elischer 
25144cf49a43SJulian Elischer /************************************************************************
2515069154d5SJulian Elischer 			Queue element get/free routines
25164cf49a43SJulian Elischer ************************************************************************/
25174cf49a43SJulian Elischer 
25184cf49a43SJulian Elischer 
2519069154d5SJulian Elischer static int			allocated;	/* number of items malloc'd */
2520069154d5SJulian Elischer static int			maxalloc = 128;	/* limit the damage of a leak */
2521069154d5SJulian Elischer static const int		ngqfreemax = 64;/* cache at most this many */
2522069154d5SJulian Elischer static const int		ngqfreelow = 4; /* try malloc if free < this */
2523069154d5SJulian Elischer static volatile int		ngqfreesize;	/* number of cached entries */
2524069154d5SJulian Elischer #ifdef	ITEM_DEBUG
2525069154d5SJulian Elischer static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist);
2526069154d5SJulian Elischer #endif
25274cf49a43SJulian Elischer /*
25284cf49a43SJulian Elischer  * Get a queue entry
2529069154d5SJulian Elischer  * This is usually called when a packet first enters netgraph.
2530069154d5SJulian Elischer  * By definition, this is usually from an interrupt, or from a user.
2531069154d5SJulian Elischer  * Users are not so important, but try be quick for the times that it's
2532069154d5SJulian Elischer  * an interrupt. Use atomic operations to cope with collisions
2533069154d5SJulian Elischer  * with interrupts and other processors. Assumes MALLOC is SMP safe.
2534069154d5SJulian Elischer  * XXX If reserve is low, we should try to get 2 from malloc as this
2535069154d5SJulian Elischer  * would indicate it often fails.
25364cf49a43SJulian Elischer  */
2537069154d5SJulian Elischer static item_p
25384cf49a43SJulian Elischer ng_getqblk(void)
25394cf49a43SJulian Elischer {
2540069154d5SJulian Elischer 	item_p item = NULL;
25414cf49a43SJulian Elischer 
2542069154d5SJulian Elischer 	/*
2543069154d5SJulian Elischer 	 * Try get a cached queue block, or else allocate a new one
2544069154d5SJulian Elischer 	 * If we are less than our reserve, try malloc. If malloc
2545069154d5SJulian Elischer 	 * fails, then that's what the reserve is for...
2546069154d5SJulian Elischer 	 * Don't completely trust ngqfreesize, as it is subject
2547069154d5SJulian Elischer 	 * to races.. (it'll eventually catch up but may be out by one or two
2548069154d5SJulian Elischer 	 * for brief moments(under SMP or interrupts).
2549069154d5SJulian Elischer 	 * ngqfree is the final arbiter. We have our little reserve
2550069154d5SJulian Elischer 	 * because we use M_NOWAIT for malloc. This just helps us
2551069154d5SJulian Elischer 	 * avoid dropping packets while not increasing the time
2552069154d5SJulian Elischer 	 * we take to service the interrupt (on average) (we hope).
2553069154d5SJulian Elischer 	 */
2554069154d5SJulian Elischer 	for (;;) {
2555069154d5SJulian Elischer 		if ((ngqfreesize < ngqfreelow) || (ngqfree == NULL)) {
2556069154d5SJulian Elischer 			if (allocated < maxalloc) {  /* don't leak forever */
2557069154d5SJulian Elischer 				MALLOC(item, item_p ,
2558069154d5SJulian Elischer 				    sizeof(*item), M_NETGRAPH_ITEM,
2559069154d5SJulian Elischer 				    (M_NOWAIT | M_ZERO));
2560069154d5SJulian Elischer 				if (item) {
2561069154d5SJulian Elischer #ifdef	ITEM_DEBUG
2562069154d5SJulian Elischer 					TAILQ_INSERT_TAIL(&ng_itemlist,
2563069154d5SJulian Elischer 								item, all);
2564069154d5SJulian Elischer #endif	/* ITEM_DEBUG */
2565069154d5SJulian Elischer 					atomic_add_int(&allocated, 1);
2566069154d5SJulian Elischer 					break;
25674cf49a43SJulian Elischer 				}
2568069154d5SJulian Elischer 			}
2569069154d5SJulian Elischer 		}
2570069154d5SJulian Elischer 
2571069154d5SJulian Elischer 		/*
2572069154d5SJulian Elischer 		 * We didn't or couldn't malloc.
2573069154d5SJulian Elischer 		 * try get one from our cache.
2574069154d5SJulian Elischer 		 * item must be NULL to get here.
2575069154d5SJulian Elischer 		 */
2576069154d5SJulian Elischer 		if ((item = ngqfree) != NULL) {
2577069154d5SJulian Elischer 			/*
2578069154d5SJulian Elischer 			 * Atomically try grab the first item
2579069154d5SJulian Elischer 			 * and put it's successor in its place.
2580069154d5SJulian Elischer 			 * If we fail, just try again.. someone else
2581069154d5SJulian Elischer 			 * beat us to this one or freed one.
2582069154d5SJulian Elischer 			 * Don't worry about races with ngqfreesize.
2583069154d5SJulian Elischer 			 * Close enough is good enough..
2584069154d5SJulian Elischer 			 */
2585069154d5SJulian Elischer 			if (atomic_cmpset_ptr(&ngqfree, item, item->el_next)) {
2586069154d5SJulian Elischer 				atomic_subtract_int(&ngqfreesize, 1);
2587069154d5SJulian Elischer 				break;
2588069154d5SJulian Elischer 			}
2589069154d5SJulian Elischer 			item = NULL;
25904cf49a43SJulian Elischer 		} else {
2591069154d5SJulian Elischer 			/* We really ran out */
2592069154d5SJulian Elischer 			break;
25934cf49a43SJulian Elischer 		}
2594069154d5SJulian Elischer 	}
2595069154d5SJulian Elischer 	item->el_flags &= ~NGQF_FREE;
2596069154d5SJulian Elischer 	return (item);
25974cf49a43SJulian Elischer }
25984cf49a43SJulian Elischer 
25994cf49a43SJulian Elischer /*
26004cf49a43SJulian Elischer  * Release a queue entry
26014cf49a43SJulian Elischer  */
2602069154d5SJulian Elischer void
2603069154d5SJulian Elischer ng_free_item(item_p item)
2604069154d5SJulian Elischer {
2605069154d5SJulian Elischer 
2606069154d5SJulian Elischer 	/*
2607069154d5SJulian Elischer 	 * The item may hold resources on it's own. We need to free
2608069154d5SJulian Elischer 	 * these before we can free the item. What they are depends upon
2609069154d5SJulian Elischer 	 * what kind of item it is. it is important that nodes zero
2610069154d5SJulian Elischer 	 * out pointers to resources that they remove from the item
2611069154d5SJulian Elischer 	 * or we release them again here.
2612069154d5SJulian Elischer 	 */
2613069154d5SJulian Elischer 	if (item->el_flags & NGQF_FREE) {
2614069154d5SJulian Elischer 		panic(" Freeing free queue item");
2615069154d5SJulian Elischer 	}
2616069154d5SJulian Elischer 	switch (item->el_flags & NGQF_D_M) {
2617069154d5SJulian Elischer 	case NGQF_DATA:
2618069154d5SJulian Elischer 		/* If we have an mbuf and metadata still attached.. */
2619069154d5SJulian Elischer 		NG_FREE_M(_NGI_M(item));
2620069154d5SJulian Elischer 		NG_FREE_META(_NGI_META(item));
2621069154d5SJulian Elischer 		break;
2622069154d5SJulian Elischer 	case NGQF_MESG:
2623069154d5SJulian Elischer 		_NGI_RETADDR(item) = NULL;
2624069154d5SJulian Elischer 		NG_FREE_MSG(_NGI_MSG(item));
2625069154d5SJulian Elischer 		break;
2626069154d5SJulian Elischer 	}
2627069154d5SJulian Elischer 		/* If we still have a node or hook referenced... */
2628069154d5SJulian Elischer 	if (item->el_dest) {
2629069154d5SJulian Elischer 		ng_unref(item->el_dest);
2630069154d5SJulian Elischer 		item->el_dest = NULL;
2631069154d5SJulian Elischer 	}
2632069154d5SJulian Elischer 	if (item->el_hook) {
2633069154d5SJulian Elischer 		ng_unref_hook(item->el_hook);
2634069154d5SJulian Elischer 		item->el_hook = NULL;
2635069154d5SJulian Elischer 	}
2636069154d5SJulian Elischer 	item->el_flags |= NGQF_FREE;
2637069154d5SJulian Elischer 
2638069154d5SJulian Elischer 	/*
2639069154d5SJulian Elischer 	 * We have freed any resources held by the item.
2640069154d5SJulian Elischer 	 * now we can free the item itself.
2641069154d5SJulian Elischer 	 */
2642069154d5SJulian Elischer 	if (ngqfreesize < ngqfreemax) { /* don't worry about races */
2643069154d5SJulian Elischer 		for (;;) {
2644069154d5SJulian Elischer 			item->el_next = ngqfree;
2645069154d5SJulian Elischer 			if (atomic_cmpset_ptr(&ngqfree, item->el_next, item)) {
2646069154d5SJulian Elischer 				break;
2647069154d5SJulian Elischer 			}
2648069154d5SJulian Elischer 		}
2649069154d5SJulian Elischer 		atomic_add_int(&ngqfreesize, 1);
2650069154d5SJulian Elischer 	} else {
2651069154d5SJulian Elischer 		/* This is the only place that should use this Macro */
2652069154d5SJulian Elischer #ifdef	ITEM_DEBUG
2653069154d5SJulian Elischer 		TAILQ_REMOVE(&ng_itemlist, item, all);
2654069154d5SJulian Elischer #endif	/* ITEM_DEBUG */
2655069154d5SJulian Elischer 		NG_FREE_ITEM_REAL(item);
2656069154d5SJulian Elischer 		atomic_subtract_int(&allocated, 1);
2657069154d5SJulian Elischer 	}
2658069154d5SJulian Elischer }
2659069154d5SJulian Elischer 
2660069154d5SJulian Elischer #ifdef	ITEM_DEBUG
2661069154d5SJulian Elischer void
2662069154d5SJulian Elischer dumpitem(item_p item, char *file, int line)
2663069154d5SJulian Elischer {
2664069154d5SJulian Elischer 	if (item->el_flags & NGQF_FREE) {
2665069154d5SJulian Elischer 		printf(" Free item, freed at %s, line %d\n",
2666069154d5SJulian Elischer 			item->lastfile, item->lastline);
2667069154d5SJulian Elischer 	} else {
2668069154d5SJulian Elischer 		printf(" ACTIVE item, last used at %s, line %d",
2669069154d5SJulian Elischer 			item->lastfile, item->lastline);
2670069154d5SJulian Elischer 		if ((item->el_flags & NGQF_D_M) == NGQF_MESG) {
2671069154d5SJulian Elischer 			printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
2672069154d5SJulian Elischer 		} else {
2673069154d5SJulian Elischer 			printf(" - [data]\n");
2674069154d5SJulian Elischer 		}
2675069154d5SJulian Elischer 	}
2676069154d5SJulian Elischer 	printf(" problem discovered at file %s, line %d\n", file, line);
2677069154d5SJulian Elischer 	if (item->el_dest)
2678069154d5SJulian Elischer 		printf("node %X ([%x])\n",
2679069154d5SJulian Elischer 			item->el_dest, ng_node2ID(item->el_dest));
2680069154d5SJulian Elischer }
2681069154d5SJulian Elischer 
2682069154d5SJulian Elischer static int
2683069154d5SJulian Elischer sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS)
2684069154d5SJulian Elischer {
2685069154d5SJulian Elischer 	int error;
2686069154d5SJulian Elischer 	int val;
2687069154d5SJulian Elischer 	item_p item;
2688069154d5SJulian Elischer 	int i;
2689069154d5SJulian Elischer 
2690069154d5SJulian Elischer 	val = allocated;
2691069154d5SJulian Elischer 	i = 1;
2692069154d5SJulian Elischer 	error = sysctl_handle_int(oidp, &val, sizeof(int), req);
2693069154d5SJulian Elischer 	TAILQ_FOREACH(item, &ng_itemlist, all) {
2694069154d5SJulian Elischer 		if (item->el_flags & NGQF_FREE) {
2695069154d5SJulian Elischer 			printf("[%d] free item, freed at %s, line %d\n",
2696069154d5SJulian Elischer 				i++, item->lastfile, item->lastline);
2697069154d5SJulian Elischer 		} else {
2698069154d5SJulian Elischer 			printf("[%d] ACTIVE item, last used at %s, line %d",
2699069154d5SJulian Elischer 				i++, item->lastfile, item->lastline);
2700069154d5SJulian Elischer 			if ((item->el_flags & NGQF_D_M) == NGQF_MESG) {
2701069154d5SJulian Elischer 				printf(" - retaddr[%d]:\n", _NGI_RETADDR(item));
2702069154d5SJulian Elischer 			} else {
2703069154d5SJulian Elischer 				printf(" - [data]\n");
2704069154d5SJulian Elischer 			}
2705069154d5SJulian Elischer 		}
2706069154d5SJulian Elischer 		if (item->el_dest) {
2707069154d5SJulian Elischer 			printf("node %X ([%x])",
2708069154d5SJulian Elischer 				item->el_dest, ng_node2ID(item->el_dest));
2709069154d5SJulian Elischer 			printf("<%X>\n",item->el_dest->input_queue.q_flags);
2710069154d5SJulian Elischer 		}
2711069154d5SJulian Elischer 	}
2712069154d5SJulian Elischer 	return error;
2713069154d5SJulian Elischer }
2714069154d5SJulian Elischer 
2715069154d5SJulian Elischer SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RD,
2716069154d5SJulian Elischer     0, 0, sysctl_debug_ng_dump_items, "I", "Number of allocated items");
2717069154d5SJulian Elischer #endif	/* ITEM_DEBUG */
2718069154d5SJulian Elischer 
2719069154d5SJulian Elischer 
2720069154d5SJulian Elischer /***********************************************************************
2721069154d5SJulian Elischer * Worklist routines
2722069154d5SJulian Elischer **********************************************************************/
2723069154d5SJulian Elischer /* NETISR thread enters here */
2724069154d5SJulian Elischer /*
2725069154d5SJulian Elischer  * Pick a node off the list of nodes with work,
2726069154d5SJulian Elischer  * try get an item to process off it.
2727069154d5SJulian Elischer  * If there are no more, remove the node from the list.
2728069154d5SJulian Elischer  */
2729069154d5SJulian Elischer static void
2730069154d5SJulian Elischer ngintr(void)
2731069154d5SJulian Elischer {
2732069154d5SJulian Elischer 	item_p item;
2733069154d5SJulian Elischer 	node_p  node = NULL;
2734069154d5SJulian Elischer 
2735069154d5SJulian Elischer 	for (;;) {
2736069154d5SJulian Elischer 		mtx_enter(&ng_worklist_mtx, MTX_SPIN);
2737069154d5SJulian Elischer 		node = TAILQ_FIRST(&ng_worklist);
2738069154d5SJulian Elischer 		if (!node) {
2739069154d5SJulian Elischer 			mtx_exit(&ng_worklist_mtx, MTX_SPIN);
2740069154d5SJulian Elischer 			break;
2741069154d5SJulian Elischer 		}
2742069154d5SJulian Elischer 		TAILQ_REMOVE(&ng_worklist, node, work);
2743069154d5SJulian Elischer 		mtx_exit(&ng_worklist_mtx, MTX_SPIN);
2744069154d5SJulian Elischer 		/*
2745069154d5SJulian Elischer 		 * We have the node. We also take over the reference
2746069154d5SJulian Elischer 		 * that the list had on it.
2747069154d5SJulian Elischer 		 * Now process as much as you can, until it won't
2748069154d5SJulian Elischer 		 * let you have another item off the queue.
2749069154d5SJulian Elischer 		 * All this time, keep the reference
2750069154d5SJulian Elischer 		 * that lets us be sure that the node still exists.
2751069154d5SJulian Elischer 		 * Let the reference go at the last minute.
2752069154d5SJulian Elischer 		 */
2753069154d5SJulian Elischer 		for (;;) {
2754069154d5SJulian Elischer 			mtx_enter(&node->input_queue.q_mtx, MTX_SPIN);
2755069154d5SJulian Elischer 			item = ng_dequeue(&node->input_queue);
2756069154d5SJulian Elischer 			if (item == NULL) {
2757069154d5SJulian Elischer 				/*
2758069154d5SJulian Elischer 				 * Say we are on the queue as long as
2759069154d5SJulian Elischer 				 * we are processing it here.
2760069154d5SJulian Elischer 				 * it probably wouldn't come here while we
2761069154d5SJulian Elischer 				 * are processing anyhow.
2762069154d5SJulian Elischer 				 */
2763069154d5SJulian Elischer 				node->flags &= ~NG_WORKQ;
2764069154d5SJulian Elischer 				mtx_exit(&node->input_queue.q_mtx, MTX_SPIN);
2765069154d5SJulian Elischer 				ng_unref(node);
2766069154d5SJulian Elischer 				break; /* go look for another node */
2767069154d5SJulian Elischer 			} else {
2768069154d5SJulian Elischer 				mtx_exit(&node->input_queue.q_mtx, MTX_SPIN);
2769069154d5SJulian Elischer #ifdef	ITEM_DEBUG
2770069154d5SJulian Elischer         _ngi_check(item, __FILE__, __LINE__);
2771069154d5SJulian Elischer #endif
2772069154d5SJulian Elischer 				ng_apply_item(node, item);
2773069154d5SJulian Elischer 			}
2774069154d5SJulian Elischer 		}
2775069154d5SJulian Elischer 	}
2776069154d5SJulian Elischer }
2777069154d5SJulian Elischer 
2778069154d5SJulian Elischer static void
2779069154d5SJulian Elischer ng_worklist_remove(node_p node)
2780069154d5SJulian Elischer {
2781069154d5SJulian Elischer 	mtx_enter(&ng_worklist_mtx, MTX_SPIN);
2782069154d5SJulian Elischer 	if (node->flags & NG_WORKQ) {
2783069154d5SJulian Elischer 		TAILQ_REMOVE(&ng_worklist, node, work);
2784069154d5SJulian Elischer 		ng_unref(node);
2785069154d5SJulian Elischer 	}
2786069154d5SJulian Elischer 	node->flags &= ~NG_WORKQ;
2787069154d5SJulian Elischer 	mtx_exit(&ng_worklist_mtx, MTX_SPIN);
2788069154d5SJulian Elischer }
2789069154d5SJulian Elischer 
2790069154d5SJulian Elischer static void
2791069154d5SJulian Elischer ng_setisr(node_p node)
2792069154d5SJulian Elischer {
2793069154d5SJulian Elischer 	mtx_enter(&ng_worklist_mtx, MTX_SPIN);
2794069154d5SJulian Elischer 	if ((node->flags & NG_WORKQ) == 0) {
2795069154d5SJulian Elischer 		/*
2796069154d5SJulian Elischer 		 * If we are not already on the work queue,
2797069154d5SJulian Elischer 		 * then put us on.
2798069154d5SJulian Elischer 		 */
2799069154d5SJulian Elischer 		node->flags |= NG_WORKQ;
2800069154d5SJulian Elischer 		TAILQ_INSERT_TAIL(&ng_worklist, node, work);
2801069154d5SJulian Elischer 		node->refs++;
2802069154d5SJulian Elischer 	}
2803069154d5SJulian Elischer 	mtx_exit(&ng_worklist_mtx, MTX_SPIN);
2804069154d5SJulian Elischer 	schednetisr(NETISR_NETGRAPH);
2805069154d5SJulian Elischer }
2806069154d5SJulian Elischer 
2807069154d5SJulian Elischer 
2808069154d5SJulian Elischer /***********************************************************************
2809069154d5SJulian Elischer * Externally useable functions to set up a queue item ready for sending
2810069154d5SJulian Elischer ***********************************************************************/
2811069154d5SJulian Elischer 
2812069154d5SJulian Elischer #ifdef	ITEM_DEBUG
2813069154d5SJulian Elischer #define	DEBUG_CHECKS							\
28144cf49a43SJulian Elischer 	do {								\
2815069154d5SJulian Elischer 		if (item->el_dest ) {					\
2816069154d5SJulian Elischer 			printf("item already has node");		\
2817069154d5SJulian Elischer 			Debugger("has node");				\
2818069154d5SJulian Elischer 			ng_unref(item->el_dest);			\
2819069154d5SJulian Elischer 			item->el_dest = NULL;				\
2820069154d5SJulian Elischer 		}							\
2821069154d5SJulian Elischer 		if (item->el_hook ) {					\
2822069154d5SJulian Elischer 			printf("item already has hook");		\
2823069154d5SJulian Elischer 			Debugger("has hook");				\
2824069154d5SJulian Elischer 			ng_unref_hook(item->el_hook);			\
2825069154d5SJulian Elischer 			item->el_hook = NULL;				\
2826069154d5SJulian Elischer 		}							\
2827069154d5SJulian Elischer 	} while (0)
2828069154d5SJulian Elischer #else
2829069154d5SJulian Elischer #define DEBUG_CHECKS
2830069154d5SJulian Elischer #endif
2831069154d5SJulian Elischer 
2832069154d5SJulian Elischer /*
2833069154d5SJulian Elischer  * Put elements into the item.
2834069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
2835069154d5SJulian Elischer  * (or equivalent)
2836069154d5SJulian Elischer  * (XXX) Unsafe because no reference held by peer on remote node.
2837069154d5SJulian Elischer  * remote node might go away in this timescale.
2838069154d5SJulian Elischer  * We know the hooks can't go away because that would require getting
2839069154d5SJulian Elischer  * a writer item on both nodes and we must have at least a  reader
2840069154d5SJulian Elischer  * here to eb able to do this.
2841069154d5SJulian Elischer  * Note that the hook loaded is the REMOTE hook.
2842069154d5SJulian Elischer  *
2843069154d5SJulian Elischer  * This is possibly in the critical path for new data.
2844069154d5SJulian Elischer  */
2845069154d5SJulian Elischer item_p
2846069154d5SJulian Elischer ng_package_data(struct mbuf *m, meta_p meta)
2847069154d5SJulian Elischer {
2848069154d5SJulian Elischer 	item_p item;
2849069154d5SJulian Elischer 
2850069154d5SJulian Elischer 	if ((item = ng_getqblk()) == NULL) {
2851069154d5SJulian Elischer 		NG_FREE_M(m);
2852069154d5SJulian Elischer 		NG_FREE_META(meta);
2853069154d5SJulian Elischer 		return (NULL);
2854069154d5SJulian Elischer 	}
2855069154d5SJulian Elischer 	DEBUG_CHECKS;
2856069154d5SJulian Elischer 	item->el_flags = NGQF_DATA;
2857069154d5SJulian Elischer 	item->el_next = NULL;
2858069154d5SJulian Elischer 	NGI_M(item) = m;
2859069154d5SJulian Elischer 	NGI_META(item) = meta;
2860069154d5SJulian Elischer 	return (item);
2861069154d5SJulian Elischer }
2862069154d5SJulian Elischer 
2863069154d5SJulian Elischer /*
2864069154d5SJulian Elischer  * Allocate a queue item and put items into it..
2865069154d5SJulian Elischer  * Evaluate the address as this will be needed to queue it and
2866069154d5SJulian Elischer  * to work out what some of the fields should be.
2867069154d5SJulian Elischer  * Hook and node references will be removed when the item is dequeued.
2868069154d5SJulian Elischer  * (or equivalent)
2869069154d5SJulian Elischer  */
2870069154d5SJulian Elischer item_p
2871069154d5SJulian Elischer ng_package_msg(struct ng_mesg *msg)
2872069154d5SJulian Elischer {
2873069154d5SJulian Elischer 	item_p item;
2874069154d5SJulian Elischer 
2875069154d5SJulian Elischer 	if ((item = ng_getqblk()) == NULL) {
2876069154d5SJulian Elischer 		if ((msg->header.flags & NGF_STATIC) == 0) {
2877069154d5SJulian Elischer 			NG_FREE_MSG(msg);
2878069154d5SJulian Elischer 		}
2879069154d5SJulian Elischer 		return (NULL);
2880069154d5SJulian Elischer 	}
2881069154d5SJulian Elischer 	DEBUG_CHECKS;
2882069154d5SJulian Elischer 	item->el_flags = NGQF_MESG;
2883069154d5SJulian Elischer 	item->el_next = NULL;
2884069154d5SJulian Elischer 	/*
2885069154d5SJulian Elischer 	 * Set the current lasthook into the queue item
2886069154d5SJulian Elischer 	 */
2887069154d5SJulian Elischer 	NGI_MSG(item) = msg;
2888069154d5SJulian Elischer 	NGI_RETADDR(item) = NULL;
2889069154d5SJulian Elischer 	return (item);
2890069154d5SJulian Elischer }
2891069154d5SJulian Elischer 
2892069154d5SJulian Elischer 
2893069154d5SJulian Elischer 
2894069154d5SJulian Elischer #define SET_RETADDR							\
2895069154d5SJulian Elischer 	do {	/* Data items don't have retaddrs */			\
2896069154d5SJulian Elischer 		if ((item->el_flags & NGQF_D_M) == NGQF_MESG) {		\
2897069154d5SJulian Elischer 			if (retaddr) {					\
2898069154d5SJulian Elischer 				NGI_RETADDR(item) = retaddr;		\
28994cf49a43SJulian Elischer 			} else {					\
2900069154d5SJulian Elischer 				/*					\
2901069154d5SJulian Elischer 				 * The old return address should be ok.	\
2902069154d5SJulian Elischer 				 * If there isn't one, use the address	\
2903069154d5SJulian Elischer 				 * here.				\
2904069154d5SJulian Elischer 				 */					\
2905069154d5SJulian Elischer 				if (NGI_RETADDR(item) == 0) {		\
2906069154d5SJulian Elischer 					NGI_RETADDR(item)		\
2907069154d5SJulian Elischer 						= ng_node2ID(here);	\
2908069154d5SJulian Elischer 				}					\
2909069154d5SJulian Elischer 			}						\
29104cf49a43SJulian Elischer 		}							\
29114cf49a43SJulian Elischer 	} while (0)
29124cf49a43SJulian Elischer 
29134cf49a43SJulian Elischer int
2914069154d5SJulian Elischer ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr)
29154cf49a43SJulian Elischer {
2916069154d5SJulian Elischer 	DEBUG_CHECKS;
2917069154d5SJulian Elischer 	/*
2918069154d5SJulian Elischer 	 * Quick sanity check..
2919069154d5SJulian Elischer 	 */
2920069154d5SJulian Elischer 	if ((hook == NULL)
2921069154d5SJulian Elischer 	|| ((hook->flags & HK_INVALID) != 0)
2922069154d5SJulian Elischer 	|| (hook->peer == NULL)
2923069154d5SJulian Elischer 	|| ((hook->peer->flags & HK_INVALID) != 0)
2924069154d5SJulian Elischer 	|| ((hook->peer->node->flags & NG_INVALID) != 0)) {
2925069154d5SJulian Elischer 		NG_FREE_ITEM(item);
2926069154d5SJulian Elischer 		return (EINVAL);
29274cf49a43SJulian Elischer 	}
29284cf49a43SJulian Elischer 
29294cf49a43SJulian Elischer 	/*
2930069154d5SJulian Elischer 	 * Transfer our interest to the other (peer) end.
2931069154d5SJulian Elischer 	 * note sleazy use of 'hook'.
29324cf49a43SJulian Elischer 	 */
2933069154d5SJulian Elischer 	item->el_hook = hook->peer;
2934069154d5SJulian Elischer 	item->el_hook->refs++;	/* don't let it go away while on the queue */
2935069154d5SJulian Elischer 	item->el_dest = hook->peer->node; /* sleaze */
2936069154d5SJulian Elischer 	item->el_dest->refs++; /* XXX dangerous, not atomic */
2937069154d5SJulian Elischer 	SET_RETADDR;
2938069154d5SJulian Elischer 	return (0);
2939069154d5SJulian Elischer }
2940069154d5SJulian Elischer 
29414cf49a43SJulian Elischer int
2942069154d5SJulian Elischer ng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr)
29434cf49a43SJulian Elischer {
29444cf49a43SJulian Elischer 	node_p  dest = NULL;
2945069154d5SJulian Elischer 	hook_p	hook = NULL;
29464cf49a43SJulian Elischer 	int     error;
2947069154d5SJulian Elischer 
2948069154d5SJulian Elischer 	DEBUG_CHECKS;
2949069154d5SJulian Elischer 	/*
2950069154d5SJulian Elischer 	 * Note that ng_path2noderef increments the reference count
2951069154d5SJulian Elischer 	 * on the node for us if it finds one. So we don't have to.
2952069154d5SJulian Elischer 	 */
2953069154d5SJulian Elischer 	error = ng_path2noderef(here, address, &dest, &hook);
2954069154d5SJulian Elischer 	if (error) {
2955069154d5SJulian Elischer 		NG_FREE_ITEM(item);
2956069154d5SJulian Elischer 		return (EINVAL);
2957069154d5SJulian Elischer 	}
2958069154d5SJulian Elischer 	item->el_dest = dest;
2959069154d5SJulian Elischer 	if (( item->el_hook = hook))
2960069154d5SJulian Elischer 		hook->refs++;	/* don't let it go away while on the queue */
2961069154d5SJulian Elischer 	SET_RETADDR;
2962069154d5SJulian Elischer 	return (0);
2963069154d5SJulian Elischer }
2964069154d5SJulian Elischer 
2965069154d5SJulian Elischer int
2966069154d5SJulian Elischer ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr)
2967069154d5SJulian Elischer {
2968069154d5SJulian Elischer 	node_p dest;
2969069154d5SJulian Elischer 
2970069154d5SJulian Elischer 	DEBUG_CHECKS;
2971069154d5SJulian Elischer 	/*
2972069154d5SJulian Elischer 	 * Find the target node.
2973069154d5SJulian Elischer 	 */
2974069154d5SJulian Elischer 	dest = ng_ID2noderef(ID); /* GETS REFERENCE! */
2975069154d5SJulian Elischer 	if (dest == NULL) {
2976069154d5SJulian Elischer 		NG_FREE_ITEM(item);
2977069154d5SJulian Elischer 		return(EINVAL);
2978069154d5SJulian Elischer 	}
2979069154d5SJulian Elischer 	/* Fill out the contents */
2980069154d5SJulian Elischer 	item->el_flags = NGQF_MESG;
2981069154d5SJulian Elischer 	item->el_next = NULL;
2982069154d5SJulian Elischer 	item->el_dest = dest;
2983069154d5SJulian Elischer 	item->el_hook = NULL;
2984069154d5SJulian Elischer 	/* NGI_RETADDR(item) = ng_node2ID(here); not sure why its here XXX */
2985069154d5SJulian Elischer 	SET_RETADDR;
2986069154d5SJulian Elischer 	return (0);
2987069154d5SJulian Elischer }
2988069154d5SJulian Elischer 
2989069154d5SJulian Elischer /*
2990069154d5SJulian Elischer  * special case to send a message to self (e.g. destroy node)
2991069154d5SJulian Elischer  * Possibly indicate an arrival hook too.
2992069154d5SJulian Elischer  * Useful for removing that hook :-)
2993069154d5SJulian Elischer  */
2994069154d5SJulian Elischer item_p
2995069154d5SJulian Elischer ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg)
2996069154d5SJulian Elischer {
2997069154d5SJulian Elischer 	item_p item;
29984cf49a43SJulian Elischer 
2999859a4d16SJulian Elischer 	/*
3000859a4d16SJulian Elischer 	 * Find the target node.
3001859a4d16SJulian Elischer 	 * If there is a HOOK argument, then use that in preference
3002859a4d16SJulian Elischer 	 * to the address.
3003859a4d16SJulian Elischer 	 */
3004069154d5SJulian Elischer 	if ((item = ng_getqblk()) == NULL) {
3005069154d5SJulian Elischer 		if ((msg->header.flags & NGF_STATIC) == 0) {
3006069154d5SJulian Elischer 			NG_FREE_MSG(msg);
30074cf49a43SJulian Elischer 		}
3008069154d5SJulian Elischer 		return (NULL);
30094cf49a43SJulian Elischer 	}
30104cf49a43SJulian Elischer 
30114cf49a43SJulian Elischer 	/* Fill out the contents */
3012069154d5SJulian Elischer 	item->el_flags = NGQF_MESG;
3013069154d5SJulian Elischer 	item->el_next = NULL;
3014069154d5SJulian Elischer 	item->el_dest = here;
3015069154d5SJulian Elischer 	here->refs++; /* XXX  not atomic, + May have other races */
3016069154d5SJulian Elischer 	item->el_hook = hook;
3017069154d5SJulian Elischer 	if (hook)
3018069154d5SJulian Elischer 		hook->refs++;
3019069154d5SJulian Elischer 	NGI_MSG(item) = msg;
3020069154d5SJulian Elischer 	NGI_RETADDR(item) = ng_node2ID(here);
3021069154d5SJulian Elischer 	return (item);
30224cf49a43SJulian Elischer }
30234cf49a43SJulian Elischer 
30244cf49a43SJulian Elischer /*
3025069154d5SJulian Elischer  * Set the address, if none given, give the node here.
30264cf49a43SJulian Elischer  */
3027069154d5SJulian Elischer void
3028069154d5SJulian Elischer ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr)
30294cf49a43SJulian Elischer {
3030069154d5SJulian Elischer 	if (retaddr) {
3031069154d5SJulian Elischer 		NGI_RETADDR(item) = retaddr;
3032069154d5SJulian Elischer 	} else {
3033069154d5SJulian Elischer 		/*
3034069154d5SJulian Elischer 		 * The old return address should be ok.
3035069154d5SJulian Elischer 		 * If there isn't one, use the address here.
3036069154d5SJulian Elischer 		 */
3037069154d5SJulian Elischer 		NGI_RETADDR(item) = ng_node2ID(here);
3038069154d5SJulian Elischer 	}
3039069154d5SJulian Elischer }
3040069154d5SJulian Elischer 
3041069154d5SJulian Elischer #define TESTING
3042069154d5SJulian Elischer #ifdef TESTING
3043069154d5SJulian Elischer /* just test all the macros */
3044069154d5SJulian Elischer void
3045069154d5SJulian Elischer ng_macro_test(item_p item);
3046069154d5SJulian Elischer void
3047069154d5SJulian Elischer ng_macro_test(item_p item)
3048069154d5SJulian Elischer {
3049069154d5SJulian Elischer 	node_p node = NULL;
3050069154d5SJulian Elischer 	hook_p hook = NULL;
30514cf49a43SJulian Elischer 	struct mbuf *m;
30524cf49a43SJulian Elischer 	meta_p meta;
30534cf49a43SJulian Elischer 	struct ng_mesg *msg;
3054069154d5SJulian Elischer 	ng_ID_t retaddr;
3055069154d5SJulian Elischer 	int	error;
30564cf49a43SJulian Elischer 
3057069154d5SJulian Elischer 	NGI_GET_M(item, m);
3058069154d5SJulian Elischer 	NGI_GET_META(item, meta);
3059069154d5SJulian Elischer 	NGI_GET_MSG(item, msg);
3060069154d5SJulian Elischer 	retaddr = NGI_RETADDR(item);
3061069154d5SJulian Elischer 	NG_SEND_DATA(error, hook, m, meta);
3062069154d5SJulian Elischer 	NG_SEND_DATA_ONLY(error, hook, m);
3063069154d5SJulian Elischer 	NG_FWD_NEW_DATA(error, item, hook, m);
3064069154d5SJulian Elischer 	NG_FWD_DATA(error, item, hook);
3065069154d5SJulian Elischer 	NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr);
3066069154d5SJulian Elischer 	NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr);
3067069154d5SJulian Elischer 	NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr);
3068069154d5SJulian Elischer 	NG_QUEUE_MSG(error, node, msg, ".:", retaddr);
3069069154d5SJulian Elischer 	NG_FWD_MSG_HOOK(error, node, item, hook, retaddr);
30704cf49a43SJulian Elischer }
3071069154d5SJulian Elischer #endif /* TESTING */
30724cf49a43SJulian Elischer 
3073