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