ng_base.c revision 93818
118334Speter/* 290285Sobrien * ng_base.c 3169699Skan * 4169699Skan * Copyright (c) 1996-1999 Whistle Communications, Inc. 518334Speter * All rights reserved. 690285Sobrien * 718334Speter * Subject to the following obligations and disclaimer of warranty, use and 890285Sobrien * redistribution of this software, in source or object code forms, with or 990285Sobrien * without modifications are expressly permitted by Whistle Communications; 1090285Sobrien * provided, however, that: 1190285Sobrien * 1. Any and all reproductions of the source or object code must include the 1218334Speter * copyright notice above and the following disclaimer of warranties; and 1390285Sobrien * 2. No rights are granted, in any manner or form, to use Whistle 1490285Sobrien * Communications, Inc. trademarks, including the mark "WHISTLE 1590285Sobrien * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1690285Sobrien * such appears in the above copyright notice or in the software. 1718334Speter * 1818334Speter * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 1990285Sobrien * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 20169699Skan * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 21169699Skan * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2218334Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 2318334Speter * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2418334Speter * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 2518334Speter * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2618334Speter * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 2718334Speter * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 2818334Speter * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 2918334Speter * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 3018334Speter * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 31169699Skan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3218334Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3318334Speter * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3418334Speter * OF SUCH DAMAGE. 3518334Speter * 3618334Speter * Authors: Julian Elischer <julian@freebsd.org> 3718334Speter * Archie Cobbs <archie@freebsd.org> 3818334Speter * 3918334Speter * $FreeBSD: head/sys/netgraph/ng_base.c 93818 2002-04-04 21:03:38Z jhb $ 4018334Speter * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $ 4118334Speter */ 4218334Speter 4318334Speter/* 4418334Speter * This file implements the base netgraph code. 4518334Speter */ 4618334Speter 4718334Speter#include <sys/param.h> 4818334Speter#include <sys/systm.h> 4918334Speter#include <sys/errno.h> 5018334Speter#include <sys/kernel.h> 5118334Speter#include <sys/malloc.h> 5218334Speter#include <sys/syslog.h> 5318334Speter#include <sys/sysctl.h> 5418334Speter#include <sys/linker.h> 5518334Speter#include <sys/queue.h> 5618334Speter#include <sys/mbuf.h> 5718334Speter#include <sys/ctype.h> 5818334Speter#include <sys/sysctl.h> 5918334Speter#include <machine/limits.h> 6018334Speter 6118334Speter#include <net/netisr.h> 6218334Speter 6318334Speter#include <netgraph/ng_message.h> 6418334Speter#include <netgraph/netgraph.h> 6518334Speter#include <netgraph/ng_parse.h> 6618334Speter 6718334SpeterMODULE_VERSION(netgraph, NG_ABI_VERSION); 6818334Speter 6918334Speter/* List of all active nodes */ 7018334Speterstatic LIST_HEAD(, ng_node) ng_nodelist; 7118334Speterstatic struct mtx ng_nodelist_mtx; 7218334Speter 7318334Speter#ifdef NETGRAPH_DEBUG 7418334Speter 7518334Speterstatic SLIST_HEAD(, ng_node) ng_allnodes; 7618334Speterstatic LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */ 7718334Speterstatic SLIST_HEAD(, ng_hook) ng_allhooks; 7818334Speterstatic LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */ 7918334Speter 8018334Speterstatic void ng_dumpitems(void); 8118334Speterstatic void ng_dumpnodes(void); 8218334Speterstatic void ng_dumphooks(void); 8318334Speter 8418334Speter#endif /* NETGRAPH_DEBUG */ 8518334Speter/* 8618334Speter * DEAD versions of the structures. 8718334Speter * In order to avoid races, it is sometimes neccesary to point 8818334Speter * at SOMETHING even though theoretically, the current entity is 8918334Speter * INVALID. Use these to avoid these races. 9018334Speter */ 9118334Speterstruct ng_type ng_deadtype = { 9250605Sobrien NG_ABI_VERSION, 93132727Skan "dead", 94132727Skan NULL, /* modevent */ 9518334Speter NULL, /* constructor */ 9690285Sobrien NULL, /* rcvmsg */ 9718334Speter NULL, /* shutdown */ 9890285Sobrien NULL, /* newhook */ 9990285Sobrien NULL, /* findhook */ 10018334Speter NULL, /* connect */ 10118334Speter NULL, /* rcvdata */ 10218334Speter NULL, /* disconnect */ 103169699Skan NULL, /* cmdlist */ 10418334Speter}; 10518334Speter 10618334Speterstruct ng_node ng_deadnode = { 10718334Speter "dead", 10890285Sobrien &ng_deadtype, 10950605Sobrien NG_INVALID, 110132727Skan 1, /* refs */ 111169699Skan 0, /* numhooks */ 11218334Speter NULL, /* private */ 113169699Skan 0, /* ID */ 114169699Skan LIST_HEAD_INITIALIZER(ng_deadnode.hooks), 115169699Skan {}, /* all_nodes list entry */ 116169699Skan {}, /* id hashtable list entry */ 117169699Skan {}, /* workqueue entry */ 11850605Sobrien { 0, 119169699Skan {}, /* should never use! (should hang) */ 120169699Skan NULL, 121169699Skan &ng_deadnode.nd_input_queue.queue, 122169699Skan &ng_deadnode 123169699Skan }, 124169699Skan#ifdef NETGRAPH_DEBUG 12518334Speter ND_MAGIC, 12690285Sobrien __FILE__, 12790285Sobrien __LINE__, 12818334Speter {NULL} 12990285Sobrien#endif /* NETGRAPH_DEBUG */ 13018334Speter}; 13118334Speter 13218334Speterstruct ng_hook ng_deadhook = { 13318334Speter "dead", 13418334Speter NULL, /* private */ 13518334Speter HK_INVALID | HK_DEAD, 13618334Speter 1, /* refs always >= 1 */ 13718334Speter &ng_deadhook, /* Peer is self */ 13818334Speter &ng_deadnode, /* attached to deadnode */ 13918334Speter {}, /* hooks list */ 14018334Speter NULL, /* override rcvmsg() */ 14118334Speter NULL, /* override rcvdata() */ 14218334Speter#ifdef NETGRAPH_DEBUG 14318334Speter HK_MAGIC, 14418334Speter __FILE__, 14518334Speter __LINE__, 14618334Speter {NULL} 14718334Speter#endif /* NETGRAPH_DEBUG */ 14818334Speter}; 14918334Speter 15018334Speter/* 15118334Speter * END DEAD STRUCTURES 15218334Speter */ 15318334Speter/* List nodes with unallocated work */ 15418334Speterstatic TAILQ_HEAD(, ng_node) ng_worklist = TAILQ_HEAD_INITIALIZER(ng_worklist); 15518334Speterstatic struct mtx ng_worklist_mtx; /* MUST LOCK NODE FIRST */ 15618334Speter 15718334Speter/* List of installed types */ 15818334Speterstatic LIST_HEAD(, ng_type) ng_typelist; 15918334Speterstatic struct mtx ng_typelist_mtx; 16018334Speter 16118334Speter/* Hash related definitions */ 16218334Speter/* XXX Don't need to initialise them because it's a LIST */ 16318334Speter#define NG_ID_HASH_SIZE 32 /* most systems wont need even this many */ 16418334Speterstatic LIST_HEAD(, ng_node) ng_ID_hash[NG_ID_HASH_SIZE]; 16518334Speterstatic struct mtx ng_idhash_mtx; 16650605Sobrien/* Method to find a node.. used twice so do it here */ 16750605Sobrien#define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE)) 16850605Sobrien#define NG_IDHASH_FIND(ID, node) \ 16950605Sobrien do { \ 17018334Speter LIST_FOREACH(node, &ng_ID_hash[NG_IDHASH_FN(ID)], \ 17118334Speter nd_idnodes) { \ 17218334Speter if (NG_NODE_IS_VALID(node) \ 17318334Speter && (NG_NODE_ID(node) == ID)) { \ 17418334Speter break; \ 17518334Speter } \ 17690285Sobrien } \ 17718334Speter } while (0) 17890285Sobrien 17918334Speter/* Mutex that protects the free queue item list */ 18018334Speterstatic volatile item_p ngqfree; /* free ones */ 18118334Speterstatic struct mtx ngq_mtx; 18218334Speter 18318334Speter/* Internal functions */ 18418334Speterstatic int ng_add_hook(node_p node, const char *name, hook_p * hookp); 185132727Skanstatic int ng_generic_msg(node_p here, item_p item, hook_p lasthook); 18618334Speterstatic ng_ID_t ng_decodeidname(const char *name); 18718334Speterstatic int ngb_mod_event(module_t mod, int event, void *data); 18818334Speterstatic void ng_worklist_remove(node_p node); 18918334Speterstatic void ngintr(void); 19018334Speterstatic int ng_apply_item(node_p node, item_p item); 19118334Speterstatic void ng_flush_input_queue(struct ng_queue * ngq); 19218334Speterstatic void ng_setisr(node_p node); 19318334Speterstatic node_p ng_ID2noderef(ng_ID_t ID); 19418334Speterstatic int ng_con_nodes(node_p node, const char *name, node_p node2, 19518334Speter const char *name2); 19618334Speterstatic void ng_con_part2(node_p node, hook_p hook, void *arg1, int arg2); 19718334Speterstatic void ng_con_part3(node_p node, hook_p hook, void *arg1, int arg2); 19818334Speterstatic int ng_mkpeer(node_p node, const char *name, 19918334Speter const char *name2, char *type); 20018334Speter 20150605Sobrien/* imported , these used to be externally visible, some may go back */ 20218334Speterint ng_bypass(hook_p hook1, hook_p hook2); 20318334Spetervoid ng_destroy_hook(hook_p hook); 20418334Speternode_p ng_name2noderef(node_p node, const char *name); 20518334Speterint ng_path2noderef(node_p here, const char *path, 20618334Speter node_p *dest, hook_p *lasthook); 20718334Speterstruct ng_type *ng_findtype(const char *type); 20818334Speterint ng_make_node(const char *type, node_p *nodepp); 20918334Speterint ng_path_parse(char *addr, char **node, char **path, char **hook); 21018334Spetervoid ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3); 21118334Spetervoid ng_unname(node_p node); 21218334Speter 21318334Speter 21418334Speter/* Our own netgraph malloc type */ 215169699SkanMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); 216169699SkanMALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures"); 21718334SpeterMALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures"); 21818334SpeterMALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures"); 21918334SpeterMALLOC_DEFINE(M_NETGRAPH_META, "netgraph_meta", "netgraph name storage"); 22018334SpeterMALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage"); 22118334Speter 22218334Speter/* Should not be visible outside this file */ 22318334Speter 22418334Speter#define _NG_ALLOC_HOOK(hook) \ 22518334Speter MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO) 22618334Speter#define _NG_ALLOC_NODE(node) \ 22718334Speter MALLOC(node, node_p, sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO) 22818334Speter 22918334Speter#ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ 23018334Speter/* 23118334Speter * In debug mode: 23218334Speter * In an attempt to help track reference count screwups 23318334Speter * we do not free objects back to the malloc system, but keep them 23418334Speter * in a local cache where we can examine them and keep information safely 23518334Speter * after they have been freed. 23618334Speter * We use this scheme for nodes and hooks, and to some extent for items. 23718334Speter */ 23850605Sobrienstatic __inline hook_p 23950605Sobrienng_alloc_hook(void) 24050605Sobrien{ 24150605Sobrien hook_p hook; 24250605Sobrien SLIST_ENTRY(ng_hook) temp; 24350605Sobrien mtx_lock(&ng_nodelist_mtx); 24450605Sobrien hook = LIST_FIRST(&ng_freehooks); 24550605Sobrien if (hook) { 24650605Sobrien LIST_REMOVE(hook, hk_hooks); 247132727Skan bcopy(&hook->hk_all, &temp, sizeof(temp)); 248132727Skan bzero(hook, sizeof(struct ng_hook)); 249169699Skan bcopy(&temp, &hook->hk_all, sizeof(temp)); 250169699Skan mtx_unlock(&ng_nodelist_mtx); 251169699Skan hook->hk_magic = HK_MAGIC; 252132727Skan } else { 253132727Skan mtx_unlock(&ng_nodelist_mtx); 254132727Skan _NG_ALLOC_HOOK(hook); 255132727Skan if (hook) { 256132727Skan hook->hk_magic = HK_MAGIC; 257132727Skan mtx_lock(&ng_nodelist_mtx); 258132727Skan SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all); 259132727Skan mtx_unlock(&ng_nodelist_mtx); 260132727Skan } 261132727Skan } 262132727Skan return (hook); 263132727Skan} 264132727Skan 265132727Skanstatic __inline node_p 266132727Skanng_alloc_node(void) 267132727Skan{ 268132727Skan node_p node; 269132727Skan SLIST_ENTRY(ng_node) temp; 270132727Skan mtx_lock(&ng_nodelist_mtx); 271132727Skan node = LIST_FIRST(&ng_freenodes); 272132727Skan if (node) { 273169699Skan LIST_REMOVE(node, nd_nodes); 274169699Skan bcopy(&node->nd_all, &temp, sizeof(temp)); 275132727Skan bzero(node, sizeof(struct ng_node)); 276132727Skan bcopy(&temp, &node->nd_all, sizeof(temp)); 277132727Skan mtx_unlock(&ng_nodelist_mtx); 278132727Skan node->nd_magic = ND_MAGIC; 279132727Skan } else { 280132727Skan mtx_unlock(&ng_nodelist_mtx); 281132727Skan _NG_ALLOC_NODE(node); 282132727Skan if (node) { 283169699Skan node->nd_magic = ND_MAGIC; 284169699Skan mtx_lock(&ng_nodelist_mtx); 285169699Skan SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all); 286169699Skan mtx_unlock(&ng_nodelist_mtx); 287169699Skan } 288169699Skan } 289169699Skan return (node); 290169699Skan} 291169699Skan 292169699Skan#define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0) 293169699Skan#define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0) 294169699Skan 295169699Skan 296169699Skan#define NG_FREE_HOOK(hook) \ 297169699Skan do { \ 298169699Skan mtx_lock(&ng_nodelist_mtx); \ 299169699Skan LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks); \ 300169699Skan hook->hk_magic = 0; \ 301169699Skan mtx_unlock(&ng_nodelist_mtx); \ 302169699Skan } while (0) 30318334Speter 30418334Speter#define NG_FREE_NODE(node) \ 305117404Skan do { \ 30618334Speter mtx_lock(&ng_nodelist_mtx); \ 30718334Speter LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes); \ 30818334Speter node->nd_magic = 0; \ 30918334Speter mtx_unlock(&ng_nodelist_mtx); \ 31018334Speter } while (0) 31118334Speter 31218334Speter#else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ 31318334Speter 314132727Skan#define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook) 315132727Skan#define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node) 316132727Skan 317169699Skan#define NG_FREE_HOOK(hook) do { FREE((hook), M_NETGRAPH_HOOK); } while (0) 31818334Speter#define NG_FREE_NODE(node) do { FREE((node), M_NETGRAPH_NODE); } while (0) 31918334Speter 320169699Skan#endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ 32118334Speter 32218334Speter/* Warning: Generally use NG_FREE_ITEM() instead */ 32318334Speter#define NG_FREE_ITEM_REAL(item) do { FREE((item), M_NETGRAPH_ITEM); } while (0) 32418334Speter 32518334Speter 326169699Skan/* Set this to Debugger("X") to catch all errors as they occur */ 327169699Skan#ifndef TRAP_ERROR 328169699Skan#define TRAP_ERROR() 32918334Speter#endif 33050605Sobrien 33150605Sobrienstatic ng_ID_t nextID = 1; 33250605Sobrien 33350605Sobrien#ifdef INVARIANTS 33418334Speter#define CHECK_DATA_MBUF(m) do { \ 33518334Speter struct mbuf *n; \ 33618334Speter int total; \ 33718334Speter \ 33818334Speter if (((m)->m_flags & M_PKTHDR) == 0) \ 33918334Speter panic("%s: !PKTHDR", __func__); \ 34018334Speter for (total = 0, n = (m); n != NULL; n = n->m_next) \ 34118334Speter total += n->m_len; \ 34218334Speter if ((m)->m_pkthdr.len != total) { \ 34318334Speter panic("%s: %d != %d", \ 34418334Speter __func__, (m)->m_pkthdr.len, total); \ 34518334Speter } \ 34618334Speter } while (0) 34718334Speter#else 34818334Speter#define CHECK_DATA_MBUF(m) 34918334Speter#endif 35018334Speter 35118334Speter 35218334Speter/************************************************************************ 35318334Speter Parse type definitions for generic messages 35418334Speter************************************************************************/ 35518334Speter 356169699Skan/* Handy structure parse type defining macro */ 35718334Speter#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ 35818334Speterstatic const struct ng_parse_struct_info \ 35918334Speter ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args; \ 360169699Skanstatic const struct ng_parse_type ng_generic_ ## lo ## _type = { \ 361169699Skan &ng_parse_struct_type, \ 362169699Skan &ng_ ## lo ## _type_info \ 363169699Skan} 36418334Speter 36518334SpeterDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); 366169699SkanDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); 36718334SpeterDEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); 36818334SpeterDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); 369169699SkanDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); 370169699SkanDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); 371169699SkanDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); 37218334Speter 373169699Skan/* Get length of an array when the length is stored as a 32 bit 374169699Skan value immediately preceding the array -- as with struct namelist 37518334Speter and struct typelist. */ 37618334Speterstatic int 37718334Speterng_generic_list_getLength(const struct ng_parse_type *type, 378117404Skan const u_char *start, const u_char *buf) 37918334Speter{ 38018334Speter return *((const u_int32_t *)(buf - 4)); 38118334Speter} 38218334Speter 383169699Skan/* Get length of the array of struct linkinfo inside a struct hooklist */ 384169699Skanstatic int 385169699Skanng_generic_linkinfo_getLength(const struct ng_parse_type *type, 386169699Skan const u_char *start, const u_char *buf) 38718334Speter{ 388169699Skan const struct hooklist *hl = (const struct hooklist *)start; 389169699Skan 390169699Skan return hl->nodeinfo.hooks; 391169699Skan} 392169699Skan 39390285Sobrien/* Array type for a variable length array of struct namelist */ 394169699Skanstatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = { 395169699Skan &ng_generic_nodeinfo_type, 396169699Skan &ng_generic_list_getLength 397169699Skan}; 398169699Skanstatic const struct ng_parse_type ng_generic_nodeinfoarray_type = { 399169699Skan &ng_parse_array_type, 400169699Skan &ng_nodeinfoarray_type_info 401169699Skan}; 402169699Skan 40390285Sobrien/* Array type for a variable length array of struct typelist */ 404169699Skanstatic const struct ng_parse_array_info ng_typeinfoarray_type_info = { 405169699Skan &ng_generic_typeinfo_type, 40618334Speter &ng_generic_list_getLength 40718334Speter}; 40818334Speterstatic const struct ng_parse_type ng_generic_typeinfoarray_type = { 40918334Speter &ng_parse_array_type, 41018334Speter &ng_typeinfoarray_type_info 41118334Speter}; 41218334Speter 41318334Speter/* Array type for array of struct linkinfo in struct hooklist */ 41418334Speterstatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { 41518334Speter &ng_generic_linkinfo_type, 41618334Speter &ng_generic_linkinfo_getLength 41718334Speter}; 41818334Speterstatic const struct ng_parse_type ng_generic_linkinfo_array_type = { 41990285Sobrien &ng_parse_array_type, 42090285Sobrien &ng_generic_linkinfo_array_type_info 42118334Speter}; 422169699Skan 423169699SkanDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); 42418334SpeterDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, 42518334Speter (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); 42618334SpeterDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, 42790285Sobrien (&ng_generic_nodeinfoarray_type)); 42890285Sobrien 42990285Sobrien/* List of commands and how to convert arguments to/from ASCII */ 43090285Sobrienstatic const struct ng_cmdlist ng_generic_cmds[] = { 43190285Sobrien { 43290285Sobrien NGM_GENERIC_COOKIE, 43390285Sobrien NGM_SHUTDOWN, 43490285Sobrien "shutdown", 43590285Sobrien NULL, 436169699Skan NULL 43790285Sobrien }, 43890285Sobrien { 43918334Speter NGM_GENERIC_COOKIE, 44018334Speter NGM_MKPEER, 44190285Sobrien "mkpeer", 44218334Speter &ng_generic_mkpeer_type, 44390285Sobrien NULL 44418334Speter }, 44590285Sobrien { 44690285Sobrien NGM_GENERIC_COOKIE, 44718334Speter NGM_CONNECT, 44890285Sobrien "connect", 44990285Sobrien &ng_generic_connect_type, 45090285Sobrien NULL 45190285Sobrien }, 45290285Sobrien { 45390285Sobrien NGM_GENERIC_COOKIE, 45418334Speter NGM_NAME, 45518334Speter "name", 45618334Speter &ng_generic_name_type, 45718334Speter NULL 45850605Sobrien }, 45950605Sobrien { 46050605Sobrien NGM_GENERIC_COOKIE, 46190285Sobrien NGM_RMHOOK, 46250605Sobrien "rmhook", 46350605Sobrien &ng_generic_rmhook_type, 46450605Sobrien NULL 46550605Sobrien }, 46670639Sobrien { 46770639Sobrien NGM_GENERIC_COOKIE, 46870639Sobrien NGM_NODEINFO, 46970639Sobrien "nodeinfo", 47070639Sobrien NULL, 47170639Sobrien &ng_generic_nodeinfo_type 47270639Sobrien }, 47350605Sobrien { 47450605Sobrien NGM_GENERIC_COOKIE, 47518334Speter NGM_LISTHOOKS, 47690285Sobrien "listhooks", 47790285Sobrien NULL, 47818334Speter &ng_generic_hooklist_type 47990285Sobrien }, 48090285Sobrien { 48190285Sobrien NGM_GENERIC_COOKIE, 48290285Sobrien NGM_LISTNAMES, 48390285Sobrien "listnames", 48418334Speter NULL, 48590285Sobrien &ng_generic_listnodes_type /* same as NGM_LISTNODES */ 48690285Sobrien }, 48790285Sobrien { 48890285Sobrien NGM_GENERIC_COOKIE, 48990285Sobrien NGM_LISTNODES, 49090285Sobrien "listnodes", 49190285Sobrien NULL, 49290285Sobrien &ng_generic_listnodes_type 49390285Sobrien }, 49418334Speter { 49590285Sobrien NGM_GENERIC_COOKIE, 49618334Speter NGM_LISTTYPES, 49718334Speter "listtypes", 49818334Speter NULL, 49918334Speter &ng_generic_typeinfo_type 50018334Speter }, 50150605Sobrien { 50250605Sobrien NGM_GENERIC_COOKIE, 50318334Speter NGM_TEXT_CONFIG, 50418334Speter "textconfig", 50518334Speter NULL, 50618334Speter &ng_parse_string_type 50718334Speter }, 50818334Speter { 509169699Skan NGM_GENERIC_COOKIE, 510169699Skan NGM_TEXT_STATUS, 511169699Skan "textstatus", 512169699Skan NULL, 513169699Skan &ng_parse_string_type 514169699Skan }, 515169699Skan { 516169699Skan NGM_GENERIC_COOKIE, 517169699Skan NGM_ASCII2BINARY, 518169699Skan "ascii2binary", 519169699Skan &ng_parse_ng_mesg_type, 520169699Skan &ng_parse_ng_mesg_type 521169699Skan }, 522169699Skan { 523169699Skan NGM_GENERIC_COOKIE, 524169699Skan NGM_BINARY2ASCII, 525169699Skan "binary2ascii", 526169699Skan &ng_parse_ng_mesg_type, 527169699Skan &ng_parse_ng_mesg_type 528169699Skan }, 529169699Skan { 0 } 530169699Skan}; 531169699Skan 532169699Skan/************************************************************************ 533169699Skan Node routines 534169699Skan************************************************************************/ 535169699Skan 536169699Skan/* 537169699Skan * Instantiate a node of the requested type 538169699Skan */ 539169699Skanint 540169699Skanng_make_node(const char *typename, node_p *nodepp) 541169699Skan{ 542169699Skan struct ng_type *type; 543169699Skan int error; 544169699Skan 545169699Skan /* Check that the type makes sense */ 546169699Skan if (typename == NULL) { 547169699Skan TRAP_ERROR(); 548169699Skan return (EINVAL); 549169699Skan } 550169699Skan 551169699Skan /* Locate the node type */ 552169699Skan if ((type = ng_findtype(typename)) == NULL) { 553169699Skan char filename[NG_TYPELEN + 4]; 554169699Skan linker_file_t lf; 555169699Skan int error; 556169699Skan 557169699Skan /* Not found, try to load it as a loadable module */ 558169699Skan snprintf(filename, sizeof(filename), "ng_%s", typename); 559169699Skan error = linker_load_file(filename, &lf); 560169699Skan if (error != 0) 56118334Speter return (error); 56218334Speter lf->userrefs++; /* pretend loaded by the syscall */ 56318334Speter 56490285Sobrien /* Try again, as now the type should have linked itself in */ 56518334Speter if ((type = ng_findtype(typename)) == NULL) 56618334Speter return (ENXIO); 56718334Speter } 56818334Speter 569132727Skan /* 570132727Skan * If we have a constructor, then make the node and 57118334Speter * call the constructor to do type specific initialisation. 57218334Speter */ 57318334Speter if (type->constructor != NULL) { 57418334Speter if ((error = ng_make_node_common(type, nodepp)) == 0) { 57518334Speter if ((error = ((*type->constructor)(*nodepp)) != 0)) { 57618334Speter NG_NODE_UNREF(*nodepp); 57718334Speter } 57818334Speter } 57918334Speter } else { 58018334Speter /* 58118334Speter * Node has no constructor. We cannot ask for one 58218334Speter * to be made. It must be brought into existance by 58390285Sobrien * some external agency. The external agency should 58418334Speter * call ng_make_node_common() directly to get the 58518334Speter * netgraph part initialised. 58618334Speter */ 58718334Speter TRAP_ERROR(); 58818334Speter error = EINVAL; 58918334Speter } 59018334Speter return (error); 59190285Sobrien} 59218334Speter 59318334Speter/* 59418334Speter * Generic node creation. Called by node initialisation for externally 59518334Speter * instantiated nodes (e.g. hardware, sockets, etc ). 59618334Speter * The returned node has a reference count of 1. 59718334Speter */ 59818334Speterint 59918334Speterng_make_node_common(struct ng_type *type, node_p *nodepp) 60018334Speter{ 60118334Speter node_p node; 60218334Speter 60318334Speter /* Require the node type to have been already installed */ 60418334Speter if (ng_findtype(type->name) == NULL) { 60518334Speter TRAP_ERROR(); 60618334Speter return (EINVAL); 60718334Speter } 60818334Speter 60918334Speter /* Make a node and try attach it to the type */ 61018334Speter NG_ALLOC_NODE(node); 61118334Speter if (node == NULL) { 61218334Speter TRAP_ERROR(); 61318334Speter return (ENOMEM); 61418334Speter } 61518334Speter node->nd_type = type; 61618334Speter NG_NODE_REF(node); /* note reference */ 61718334Speter type->refs++; 61818334Speter 61918334Speter mtx_init(&node->nd_input_queue.q_mtx, "ng_node", NULL, MTX_SPIN); 62018334Speter node->nd_input_queue.queue = NULL; 62118334Speter node->nd_input_queue.last = &node->nd_input_queue.queue; 62218334Speter node->nd_input_queue.q_flags = 0; 62318334Speter node->nd_input_queue.q_node = node; 62418334Speter 62518334Speter /* Initialize hook list for new node */ 62618334Speter LIST_INIT(&node->nd_hooks); 627117404Skan 62850605Sobrien /* Link us into the node linked list */ 62918334Speter mtx_lock(&ng_nodelist_mtx); 63018334Speter LIST_INSERT_HEAD(&ng_nodelist, node, nd_nodes); 63118334Speter mtx_unlock(&ng_nodelist_mtx); 632132727Skan 633132727Skan 63418334Speter /* get an ID and put us in the hash chain */ 63518334Speter mtx_lock(&ng_idhash_mtx); 63618334Speter for (;;) { /* wrap protection, even if silly */ 63718334Speter node_p node2 = NULL; 63818334Speter node->nd_ID = nextID++; /* 137/second for 1 year before wrap */ 63918334Speter 640132727Skan /* Is there a problem with the new number? */ 64118334Speter NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */ 642132727Skan if ((node->nd_ID != 0) && (node2 == NULL)) { 64318334Speter break; 64418334Speter } 64518334Speter } 64650605Sobrien LIST_INSERT_HEAD(&ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], 647169699Skan node, nd_idnodes); 648169699Skan mtx_unlock(&ng_idhash_mtx); 649169699Skan 650169699Skan /* Done */ 651169699Skan *nodepp = node; 65250605Sobrien return (0); 653169699Skan} 654169699Skan 655132727Skan/* 65650605Sobrien * Forceably start the shutdown process on a node. Either call 657102798Skan * it's shutdown method, or do the default shutdown if there is 65850605Sobrien * no type-specific method. 65950605Sobrien * 66090285Sobrien * We can only be called form a shutdown message, so we know we have 661132727Skan * a writer lock, and therefore exclusive access. It also means 66290285Sobrien * that we should not be on the work queue, but we check anyhow. 663102798Skan * 66450605Sobrien * Persistent node types must have a type-specific method which 66550605Sobrien * Allocates a new node in which case, this one is irretrievably going away, 66650605Sobrien * or cleans up anything it needs, and just makes the node valid again, 66750605Sobrien * in which case we allow the node to survive. 668169699Skan * 669169699Skan * XXX We need to think of how to tell a persistant node that we 670169699Skan * REALLY need to go away because the hardware has gone or we 671169699Skan * are rebooting.... etc. 672169699Skan */ 673169699Skanvoid 674169699Skanng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3) 675169699Skan{ 676169699Skan hook_p hook; 677169699Skan 678169699Skan /* Check if it's already shutting down */ 679169699Skan if ((node->nd_flags & NG_CLOSING) != 0) 68050605Sobrien return; 681169699Skan 682102798Skan if (node == &ng_deadnode) { 683169699Skan printf ("shutdown called on deadnode\n"); 684102798Skan return; 685102798Skan } 686102798Skan 687102798Skan /* Add an extra reference so it doesn't go away during this */ 688102798Skan NG_NODE_REF(node); 689102798Skan 690102798Skan /* 691169699Skan * Mark it invalid so any newcomers know not to try use it 692102798Skan * Also add our own mark so we can't recurse 69350605Sobrien * note that NG_INVALID does not do this as it's also set during 69450605Sobrien * creation 695169699Skan */ 69650605Sobrien node->nd_flags |= NG_INVALID|NG_CLOSING; 69750605Sobrien 69850605Sobrien /* Notify all remaining connected nodes to disconnect */ 69950605Sobrien while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL) 70052557Sobrien ng_destroy_hook(hook); 70152557Sobrien 70252557Sobrien /* 70352557Sobrien * Drain the input queue forceably. 70452557Sobrien * it has no hooks so what's it going to do, bleed on someone? 70552557Sobrien * Theoretically we came here from a queue entry that was added 70652557Sobrien * Just before the queue was closed, so it should be empty anyway. 70790285Sobrien * Also removes us from worklist if needed. 70852557Sobrien */ 709132727Skan ng_flush_input_queue(&node->nd_input_queue); 710132727Skan 71152557Sobrien /* Ask the type if it has anything to do in this case */ 71252557Sobrien if (node->nd_type && node->nd_type->shutdown) { 71352557Sobrien (*node->nd_type->shutdown)(node); 71452557Sobrien if (NG_NODE_IS_VALID(node)) { 71552557Sobrien /* 71652557Sobrien * Well, blow me down if the node code hasn't declared 71752557Sobrien * that it doesn't want to die. 71852557Sobrien * Presumably it is a persistant node. 71952557Sobrien * If we REALLY want it to go away, 72052557Sobrien * e.g. hardware going away, 72152557Sobrien * Our caller should set NG_REALLY_DIE in nd_flags. 72252557Sobrien */ 72390285Sobrien node->nd_flags &= ~(NG_INVALID|NG_CLOSING); 72452557Sobrien NG_NODE_UNREF(node); /* Assume they still have theirs */ 72552557Sobrien return; 72652557Sobrien } 72752557Sobrien } else { /* do the default thing */ 72890285Sobrien NG_NODE_UNREF(node); 72952557Sobrien } 73090285Sobrien 73190285Sobrien ng_unname(node); /* basically a NOP these days */ 73252557Sobrien 73390285Sobrien /* 73452557Sobrien * Remove extra reference, possibly the last 73590285Sobrien * Possible other holders of references may include 73690285Sobrien * timeout callouts, but theoretically the node's supposed to 73790285Sobrien * have cancelled them. Possibly hardware dependencies may 73890285Sobrien * force a driver to 'linger' with a reference. 73990285Sobrien */ 74090285Sobrien NG_NODE_UNREF(node); 741169699Skan} 74290285Sobrien 74352557Sobrien#ifdef NETGRAPH_DEBUG 74452557Sobrienvoid 74552557Sobrienng_ref_node(node_p node) 74652557Sobrien{ 74752557Sobrien _NG_NODE_REF(node); 74852557Sobrien} 74952557Sobrien#endif 75052557Sobrien 75190285Sobrien/* 75290285Sobrien * Remove a reference to the node, possibly the last. 75352557Sobrien * deadnode always acts as it it were the last. 75452557Sobrien */ 75590285Sobrienint 75652557Sobrienng_unref_node(node_p node) 75790285Sobrien{ 75890285Sobrien int v; 759169699Skan 760169699Skan if (node == &ng_deadnode) { 76190285Sobrien return (0); 762169699Skan } 763169699Skan 76490285Sobrien do { 76590285Sobrien v = node->nd_refs - 1; 766169699Skan } while (! atomic_cmpset_int(&node->nd_refs, v + 1, v)); 76790285Sobrien 76890285Sobrien if (v == 0) { /* we were the last */ 76952557Sobrien 77052557Sobrien mtx_lock(&ng_nodelist_mtx); 77152557Sobrien node->nd_type->refs--; /* XXX maybe should get types lock? */ 772169699Skan LIST_REMOVE(node, nd_nodes); 77390285Sobrien mtx_unlock(&ng_nodelist_mtx); 77452557Sobrien 77552557Sobrien mtx_lock(&ng_idhash_mtx); 77652557Sobrien LIST_REMOVE(node, nd_idnodes); 77752557Sobrien mtx_unlock(&ng_idhash_mtx); 77852557Sobrien 77990285Sobrien mtx_destroy(&node->nd_input_queue.q_mtx); 78090285Sobrien NG_FREE_NODE(node); 78190285Sobrien } 78290285Sobrien return (v); 783132727Skan} 78490285Sobrien 78590285Sobrien/************************************************************************ 78690285Sobrien Node ID handling 78790285Sobrien************************************************************************/ 78890285Sobrienstatic node_p 78990285Sobrienng_ID2noderef(ng_ID_t ID) 79090285Sobrien{ 79190285Sobrien node_p node; 79290285Sobrien mtx_lock(&ng_idhash_mtx); 79390285Sobrien NG_IDHASH_FIND(ID, node); 79490285Sobrien if(node) 79590285Sobrien NG_NODE_REF(node); 79690285Sobrien mtx_unlock(&ng_idhash_mtx); 79790285Sobrien return(node); 79890285Sobrien} 799169699Skan 80090285Sobrienng_ID_t 80190285Sobrienng_node2ID(node_p node) 80290285Sobrien{ 80390285Sobrien return (node ? NG_NODE_ID(node) : 0); 80490285Sobrien} 80590285Sobrien 80690285Sobrien/************************************************************************ 80790285Sobrien Node name handling 80890285Sobrien************************************************************************/ 80990285Sobrien 81090285Sobrien/* 811107605Sobrien * Assign a node a name. Once assigned, the name cannot be changed. 81290285Sobrien */ 81390285Sobrienint 814169699Skanng_name_node(node_p node, const char *name) 81590285Sobrien{ 81690285Sobrien int i; 817132727Skan node_p node2; 818132727Skan 819132727Skan /* Check the name is valid */ 820132727Skan for (i = 0; i < NG_NODELEN + 1; i++) { 821132727Skan if (name[i] == '\0' || name[i] == '.' || name[i] == ':') 822132727Skan break; 823132727Skan } 824132727Skan if (i == 0 || name[i] != '\0') { 825132727Skan TRAP_ERROR(); 826132727Skan return (EINVAL); 827132727Skan } 828132727Skan if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ 829132727Skan TRAP_ERROR(); 830132727Skan return (EINVAL); 831132727Skan } 832132727Skan 833132727Skan /* Check the name isn't already being used */ 834132727Skan if ((node2 = ng_name2noderef(node, name)) != NULL) { 835132727Skan NG_NODE_UNREF(node2); 836132727Skan TRAP_ERROR(); 837132727Skan return (EADDRINUSE); 838132727Skan } 839132727Skan 840132727Skan /* copy it */ 841132727Skan strncpy(NG_NODE_NAME(node), name, NG_NODELEN); 842132727Skan 843169699Skan return (0); 844132727Skan} 845132727Skan 846132727Skan/* 847132727Skan * Find a node by absolute name. The name should NOT end with ':' 848132727Skan * The name "." means "this node" and "[xxx]" means "the node 849132727Skan * with ID (ie, at address) xxx". 850169699Skan * 851132727Skan * Returns the node if found, else NULL. 852132727Skan * Eventually should add something faster than a sequential search. 853132727Skan * Note it aquires a reference on the node so you can be sure it's still there. 854132727Skan */ 855132727Skannode_p 856132727Skanng_name2noderef(node_p here, const char *name) 857132727Skan{ 858132727Skan node_p node; 859132727Skan ng_ID_t temp; 860132727Skan 861132727Skan /* "." means "this node" */ 862132727Skan if (strcmp(name, ".") == 0) { 863132727Skan NG_NODE_REF(here); 864132727Skan return(here); 865132727Skan } 866132727Skan 86718334Speter /* Check for name-by-ID */ 86818334Speter if ((temp = ng_decodeidname(name)) != 0) { 86918334Speter return (ng_ID2noderef(temp)); 87018334Speter } 87118334Speter 87218334Speter /* Find node by name */ 873117404Skan mtx_lock(&ng_nodelist_mtx); 87418334Speter LIST_FOREACH(node, &ng_nodelist, nd_nodes) { 87518334Speter if (NG_NODE_IS_VALID(node) 87618334Speter && NG_NODE_HAS_NAME(node) 87718334Speter && (strcmp(NG_NODE_NAME(node), name) == 0)) { 87818334Speter break; 87918334Speter } 88018334Speter } 88118334Speter if (node) 88218334Speter NG_NODE_REF(node); 88318334Speter mtx_unlock(&ng_nodelist_mtx); 88418334Speter return (node); 88518334Speter} 88618334Speter 88718334Speter/* 88818334Speter * Decode a ID name, eg. "[f03034de]". Returns 0 if the 88918334Speter * string is not valid, otherwise returns the value. 89018334Speter */ 89118334Speterstatic ng_ID_t 89218334Speterng_decodeidname(const char *name) 89318334Speter{ 89418334Speter const int len = strlen(name); 89518334Speter char *eptr; 89618334Speter u_long val; 89718334Speter 89818334Speter /* Check for proper length, brackets, no leading junk */ 89918334Speter if ((len < 3) 90090285Sobrien || (name[0] != '[') 901132727Skan || (name[len - 1] != ']') 902132727Skan || (!isxdigit(name[1]))) { 903132727Skan return ((ng_ID_t)0); 904132727Skan } 90518334Speter 90690285Sobrien /* Decode number */ 90718334Speter val = strtoul(name + 1, &eptr, 16); 90818334Speter if ((eptr - name != len - 1) 90918334Speter || (val == ULONG_MAX) 91018334Speter || (val == 0)) { 91118334Speter return ((ng_ID_t)0); 91218334Speter } 91318334Speter return (ng_ID_t)val; 91418334Speter} 91518334Speter 91618334Speter/* 91718334Speter * Remove a name from a node. This should only be called 91818334Speter * when shutting down and removing the node. 91918334Speter * IF we allow name changing this may be more resurected. 92018334Speter */ 92118334Spetervoid 92290285Sobrienng_unname(node_p node) 92318334Speter{ 92418334Speter} 92518334Speter 926169699Skan/************************************************************************ 92718334Speter Hook routines 92890285Sobrien Names are not optional. Hooks are always connected, except for a 92918334Speter brief moment within these routines. On invalidation or during creation 93018334Speter they are connected to the 'dead' hook. 93118334Speter************************************************************************/ 93218334Speter 93318334Speter/* 93418334Speter * Remove a hook reference 93518334Speter */ 93618334Spetervoid 93718334Speterng_unref_hook(hook_p hook) 938169699Skan{ 93918334Speter int v; 94090285Sobrien 94118334Speter if (hook == &ng_deadhook) { 94218334Speter return; 94318334Speter } 94418334Speter do { 94518334Speter v = hook->hk_refs; 94618334Speter } while (! atomic_cmpset_int(&hook->hk_refs, v, v - 1)); 94718334Speter 94818334Speter if (v == 1) { /* we were the last */ 949169699Skan if (_NG_HOOK_NODE(hook)) { /* it'll probably be ng_deadnode */ 95090285Sobrien _NG_NODE_UNREF((_NG_HOOK_NODE(hook))); 95190285Sobrien hook->hk_node = NULL; 95290285Sobrien } 95390285Sobrien NG_FREE_HOOK(hook); 95490285Sobrien } 95518334Speter} 95690285Sobrien 95790285Sobrien/* 95890285Sobrien * Add an unconnected hook to a node. Only used internally. 95990285Sobrien * Assumes node is locked. (XXX not yet true ) 96090285Sobrien */ 96190285Sobrienstatic int 96290285Sobrienng_add_hook(node_p node, const char *name, hook_p *hookp) 96390285Sobrien{ 96418334Speter hook_p hook; 96518334Speter int error = 0; 96618334Speter 96718334Speter /* Check that the given name is good */ 96818334Speter if (name == NULL) { 96918334Speter TRAP_ERROR(); 97018334Speter return (EINVAL); 97118334Speter } 97218334Speter if (ng_findhook(node, name) != NULL) { 97318334Speter TRAP_ERROR(); 97418334Speter return (EEXIST); 97518334Speter } 97618334Speter 97718334Speter /* Allocate the hook and link it up */ 97818334Speter NG_ALLOC_HOOK(hook); 97918334Speter if (hook == NULL) { 98018334Speter TRAP_ERROR(); 98118334Speter return (ENOMEM); 98218334Speter } 98318334Speter hook->hk_refs = 1; /* add a reference for us to return */ 98418334Speter hook->hk_flags = HK_INVALID; 98518334Speter hook->hk_peer = &ng_deadhook; /* start off this way */ 98618334Speter hook->hk_node = node; 98718334Speter NG_NODE_REF(node); /* each hook counts as a reference */ 98818334Speter 98990285Sobrien /* Set hook name */ 99018334Speter strncpy(NG_HOOK_NAME(hook), name, NG_HOOKLEN); 99152557Sobrien 99252557Sobrien /* 99318334Speter * Check if the node type code has something to say about it 99452557Sobrien * If it fails, the unref of the hook will also unref the node. 99590285Sobrien */ 996117404Skan if (node->nd_type->newhook != NULL) { 997117404Skan if ((error = (*node->nd_type->newhook)(node, hook, name))) { 99818334Speter NG_HOOK_UNREF(hook); /* this frees the hook */ 99918334Speter return (error); 100018334Speter } 100118334Speter } 1002169699Skan /* 100318334Speter * The 'type' agrees so far, so go ahead and link it in. 1004169699Skan * We'll ask again later when we actually connect the hooks. 100518334Speter */ 100618334Speter LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 100718334Speter node->nd_numhooks++; 100818334Speter NG_HOOK_REF(hook); /* one for the node */ 100918334Speter 101018334Speter if (hookp) 101118334Speter *hookp = hook; 101218334Speter return (0); 101318334Speter} 1014169699Skan 101518334Speter/* 101650605Sobrien * Find a hook 101750605Sobrien * 101850605Sobrien * Node types may supply their own optimized routines for finding 101950605Sobrien * hooks. If none is supplied, we just do a linear search. 102050605Sobrien * XXX Possibly we should add a reference to the hook? 102150605Sobrien */ 102250605Sobrienhook_p 102318334Speterng_findhook(node_p node, const char *name) 1024169699Skan{ 102518334Speter hook_p hook; 102618334Speter 102718334Speter if (node->nd_type->findhook != NULL) 102890285Sobrien return (*node->nd_type->findhook)(node, name); 102918334Speter LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 103018334Speter if (NG_HOOK_IS_VALID(hook) 103118334Speter && (strcmp(NG_HOOK_NAME(hook), name) == 0)) 103218334Speter return (hook); 103318334Speter } 1034169699Skan return (NULL); 1035169699Skan} 103690285Sobrien 1037169699Skan/* 1038169699Skan * Destroy a hook 1039169699Skan * 104018334Speter * As hooks are always attached, this really destroys two hooks. 1041117404Skan * The one given, and the one attached to it. Disconnect the hooks 1042169699Skan * from each other first. We reconnect the peer hook to the 'dead' 104318334Speter * hook so that it can still exist after we depart. We then 1044117404Skan * send the peer its own destroy message. This ensures that we only 1045117404Skan * interact with the peer's structures when it is locked processing that 104618334Speter * message. We hold a reference to the peer hook so we are guaranteed that 104718334Speter * the peer hook and node are still going to exist until 104818334Speter * we are finished there as the hook holds a ref on the node. 104918334Speter * We run this same code again on the peer hook, but that time it is already 105018334Speter * attached to the 'dead' hook. 105118334Speter * 105250605Sobrien * This routine is called at all stages of hook creation 1053169699Skan * on error detection and must be able to handle any such stage. 105418334Speter */ 105518334Spetervoid 1056169699Skanng_destroy_hook(hook_p hook) 105718334Speter{ 105818334Speter hook_p peer = NG_HOOK_PEER(hook); 105918334Speter node_p node = NG_HOOK_NODE(hook); 106018334Speter 106118334Speter if (hook == &ng_deadhook) { /* better safe than sorry */ 106218334Speter printf("ng_destroy_hook called on deadhook\n"); 106318334Speter return; 106418334Speter } 106518334Speter hook->hk_flags |= HK_INVALID; /* as soon as possible */ 106618334Speter if (peer && (peer != &ng_deadhook)) { 106718334Speter /* 106850605Sobrien * Set the peer to point to ng_deadhook 106990285Sobrien * from this moment on we are effectively independent it. 107050605Sobrien * send it an rmhook message of it's own. 1071107605Sobrien */ 107218334Speter peer->hk_peer = &ng_deadhook; /* They no longer know us */ 107390285Sobrien hook->hk_peer = &ng_deadhook; /* Nor us, them */ 107490285Sobrien if (NG_HOOK_NODE(peer) == &ng_deadnode) { 1075169699Skan /* 107690285Sobrien * If it's already divorced from a node, 1077169699Skan * just free it. 107890285Sobrien */ 107990285Sobrien /* nothing */ 108090285Sobrien } else { 1081102798Skan ng_rmhook_self(peer); /* Send it a surprise */ 1082102798Skan } 108390285Sobrien NG_HOOK_UNREF(peer); /* account for peer link */ 108418334Speter NG_HOOK_UNREF(hook); /* account for peer link */ 108518334Speter } 108618334Speter 108718334Speter /* 108818334Speter * Remove the hook from the node's list to avoid possible recursion 108990285Sobrien * in case the disconnection results in node shutdown. 109090285Sobrien */ 109118334Speter if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */ 109218334Speter return; 109318334Speter } 109418334Speter LIST_REMOVE(hook, hk_hooks); 109518334Speter node->nd_numhooks--; 109618334Speter if (node->nd_type->disconnect) { 109718334Speter /* 109818334Speter * The type handler may elect to destroy the node so don't 109918334Speter * trust its existance after this point. (except 110052557Sobrien * that we still hold a reference on it. (which we 110190285Sobrien * inherrited from the hook we are destroying) 1102117404Skan */ 1103117404Skan (*node->nd_type->disconnect) (hook); 110418334Speter } 110518334Speter 110618334Speter /* 1107169699Skan * Note that because we will point to ng_deadnode, the original node 110818334Speter * is not decremented automatically so we do that manually. 1109169699Skan */ 111018334Speter _NG_HOOK_NODE(hook) = &ng_deadnode; 111150605Sobrien NG_NODE_UNREF(node); /* We no longer point to it so adjust count */ 111250605Sobrien NG_HOOK_UNREF(hook); /* Account for linkage (in list) to node */ 111350605Sobrien} 111450605Sobrien 111550605Sobrien/* 111650605Sobrien * Take two hooks on a node and merge the connection so that the given node 111750605Sobrien * is effectively bypassed. 111850605Sobrien */ 111990285Sobrienint 1120169699Skanng_bypass(hook_p hook1, hook_p hook2) 112118334Speter{ 112218334Speter if (hook1->hk_node != hook2->hk_node) { 112318334Speter TRAP_ERROR(); 112418334Speter return (EINVAL); 112518334Speter } 112618334Speter hook1->hk_peer->hk_peer = hook2->hk_peer; 1127169699Skan hook2->hk_peer->hk_peer = hook1->hk_peer; 1128169699Skan 112990285Sobrien hook1->hk_peer = &ng_deadhook; 1130169699Skan hook2->hk_peer = &ng_deadhook; 1131169699Skan 1132169699Skan /* XXX If we ever cache methods on hooks update them as well */ 113318334Speter ng_destroy_hook(hook1); 1134117404Skan ng_destroy_hook(hook2); 1135169699Skan return (0); 113618334Speter} 1137117404Skan 1138132727Skan/* 1139117404Skan * Install a new netgraph type 114018334Speter */ 114118334Speterint 114218334Speterng_newtype(struct ng_type *tp) 114318334Speter{ 114418334Speter const size_t namelen = strlen(tp->name); 114590285Sobrien 114650605Sobrien /* Check version and type name fields */ 1147169699Skan if ((tp->version != NG_ABI_VERSION) 1148169699Skan || (namelen == 0) 1149169699Skan || (namelen > NG_TYPELEN)) { 115018334Speter TRAP_ERROR(); 115118334Speter return (EINVAL); 115218334Speter } 115318334Speter 115418334Speter /* Check for name collision */ 115518334Speter if (ng_findtype(tp->name) != NULL) { 115618334Speter TRAP_ERROR(); 115718334Speter return (EEXIST); 115818334Speter } 115918334Speter 116018334Speter 1161107605Sobrien /* Link in new type */ 116218334Speter mtx_lock(&ng_typelist_mtx); 116318334Speter LIST_INSERT_HEAD(&ng_typelist, tp, types); 116418334Speter tp->refs = 1; /* first ref is linked list */ 116518334Speter mtx_unlock(&ng_typelist_mtx); 116618334Speter return (0); 116718334Speter} 116818334Speter 116918334Speter/* 117050605Sobrien * unlink a netgraph type 1171169699Skan * If no examples exist 117290285Sobrien */ 117390285Sobrienint 117490285Sobrienng_rmtype(struct ng_type *tp) 1175102798Skan{ 1176102798Skan /* Check for name collision */ 117750605Sobrien if (tp->refs != 1) { 117818334Speter TRAP_ERROR(); 117918334Speter return (EBUSY); 118018334Speter } 118118334Speter 1182169699Skan /* Unlink type */ 1183171835Skan mtx_lock(&ng_typelist_mtx); 118418334Speter LIST_REMOVE(tp, types); 118518334Speter mtx_unlock(&ng_typelist_mtx); 118618334Speter return (0); 118718334Speter} 118818334Speter 118918334Speter/* 1190169699Skan * Look for a type of the name given 119118334Speter */ 119218334Speterstruct ng_type * 119390285Sobrienng_findtype(const char *typename) 119418334Speter{ 119518334Speter struct ng_type *type; 119618334Speter 1197169699Skan mtx_lock(&ng_typelist_mtx); 119818334Speter LIST_FOREACH(type, &ng_typelist, types) { 119918334Speter if (strcmp(type->name, typename) == 0) 120090285Sobrien break; 120118334Speter } 120218334Speter mtx_unlock(&ng_typelist_mtx); 120318334Speter return (type); 1204169699Skan} 1205169699Skan 120618334Speter/************************************************************************ 1207169699Skan Composite routines 1208169699Skan************************************************************************/ 1209169699Skan/* 121018334Speter * Connect two nodes using the specified hooks, using queued functions. 121118334Speter */ 1212169699Skanstatic void 1213169699Skanng_con_part3(node_p node, hook_p hook, void *arg1, int arg2) 121418334Speter{ 121518334Speter 1216169699Skan /* 1217169699Skan * When we run, we know that the node 'node' is locked for us. 1218169699Skan * Our caller has a reference on the hook. 1219169699Skan * Our caller has a reference on the node. 1220169699Skan * (In this case our caller is ng_apply_item() ). 1221169699Skan * The peer hook has a reference on the hook. 122218334Speter * We are all set up except for the final call to the node, and 122318334Speter * the clearing of the INVALID flag. 122418334Speter */ 122518334Speter if (NG_HOOK_NODE(hook) == &ng_deadnode) { 122618334Speter /* 122718334Speter * The node must have been freed again since we last visited 122818334Speter * here. ng_destry_hook() has this effect but nothing else does. 122918334Speter * We should just release our references and 123018334Speter * free anything we can think of. 123118334Speter * Since we know it's been destroyed, and it's our caller 123218334Speter * that holds the references, just return. 123318334Speter */ 123418334Speter return ; 123518334Speter } 123618334Speter if (hook->hk_node->nd_type->connect) { 123718334Speter if ((*hook->hk_node->nd_type->connect) (hook)) { 123818334Speter ng_destroy_hook(hook); /* also zaps peer */ 123918334Speter printf("failed in ng_con_part3()\n"); 124018334Speter return ; 124118334Speter } 124218334Speter } 124318334Speter /* 124418334Speter * XXX this is wrong for SMP. Possibly we need 124518334Speter * to separate out 'create' and 'invalid' flags. 124618334Speter * should only set flags on hooks we have locked under our node. 124718334Speter */ 124818334Speter hook->hk_flags &= ~HK_INVALID; 1249169699Skan return ; 1250169699Skan} 125118334Speter 125218334Speterstatic void 125318334Speterng_con_part2(node_p node, hook_p hook, void *arg1, int arg2) 125418334Speter{ 125518334Speter 125618334Speter /* 125718334Speter * When we run, we know that the node 'node' is locked for us. 125818334Speter * Our caller has a reference on the hook. 125918334Speter * Our caller has a reference on the node. 126018334Speter * (In this case our caller is ng_apply_item() ). 1261169699Skan * The peer hook has a reference on the hook. 126218334Speter * our node pointer points to the 'dead' node. 126318334Speter * First check the hook name is unique. 126418334Speter * Should not happen because we checked before queueing this. 126518334Speter */ 126618334Speter if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) { 126718334Speter TRAP_ERROR(); 126818334Speter ng_destroy_hook(hook); /* should destroy peer too */ 126918334Speter printf("failed in ng_con_part2()\n"); 127018334Speter return ; 127118334Speter } 1272169699Skan /* 1273169699Skan * Check if the node type code has something to say about it 1274169699Skan * If it fails, the unref of the hook will also unref the attached node, 1275169699Skan * however since that node is 'ng_deadnode' this will do nothing. 1276169699Skan * The peer hook will also be destroyed. 1277169699Skan */ 1278169699Skan if (node->nd_type->newhook != NULL) { 1279169699Skan if ((*node->nd_type->newhook)(node, hook, hook->hk_name)) { 1280169699Skan ng_destroy_hook(hook); /* should destroy peer too */ 1281169699Skan printf("failed in ng_con_part2()\n"); 1282169699Skan return ; 1283169699Skan } 1284169699Skan } 1285169699Skan 1286169699Skan /* 128718334Speter * The 'type' agrees so far, so go ahead and link it in. 128818334Speter * We'll ask again later when we actually connect the hooks. 128918334Speter */ 129052557Sobrien hook->hk_node = node; /* just overwrite ng_deadnode */ 129152557Sobrien NG_NODE_REF(node); /* each hook counts as a reference */ 129252557Sobrien LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 1293169699Skan node->nd_numhooks++; 1294169699Skan NG_HOOK_REF(hook); /* one for the node */ 129518334Speter 129652557Sobrien /* 129718334Speter * We now have a symetrical situation, where both hooks have been 129818334Speter * linked to their nodes, the newhook methods have been called 129918334Speter * And the references are all correct. The hooks are still marked 130018334Speter * as invalid, as we have not called the 'connect' methods 130118334Speter * yet. 130218334Speter * We can call the local one immediatly as we have the 130318334Speter * node locked, but we need to queue the remote one. 130418334Speter */ 130518334Speter if (hook->hk_node->nd_type->connect) { 130618334Speter if ((*hook->hk_node->nd_type->connect) (hook)) { 1307169699Skan ng_destroy_hook(hook); /* also zaps peer */ 130818334Speter printf("failed in ng_con_part2(A)\n"); 130918334Speter return ; 131018334Speter } 1311169699Skan } 131218334Speter if (ng_send_fn(hook->hk_peer->hk_node, hook->hk_peer, 131318334Speter &ng_con_part3, arg1, arg2)) { 131418334Speter printf("failed in ng_con_part2(B)"); 131518334Speter ng_destroy_hook(hook); /* also zaps peer */ 131650605Sobrien return ; 1317117404Skan } 1318169699Skan hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */ 1319169699Skan return ; 1320169699Skan} 1321117404Skan 1322117404Skan/* 1323117404Skan * Connect this node with another node. We assume that this node is 1324117404Skan * currently locked, as we are only called from an NGM_CONNECT message. 132550605Sobrien */ 132650605Sobrienstatic int 132718334Speterng_con_nodes(node_p node, const char *name, node_p node2, const char *name2) 132890285Sobrien{ 132990285Sobrien int error; 133090285Sobrien hook_p hook; 133190285Sobrien hook_p hook2; 133290285Sobrien 133390285Sobrien if (ng_findhook(node2, name2) != NULL) { 133490285Sobrien return(EEXIST); 133590285Sobrien } 133690285Sobrien if ((error = ng_add_hook(node, name, &hook))) /* gives us a ref */ 133790285Sobrien return (error); 133890285Sobrien /* Allocate the other hook and link it up */ 133990285Sobrien NG_ALLOC_HOOK(hook2); 134090285Sobrien if (hook == NULL) { 134190285Sobrien TRAP_ERROR(); 134290285Sobrien ng_destroy_hook(hook); /* XXX check ref counts so far */ 134390285Sobrien NG_HOOK_UNREF(hook); /* including our ref */ 134490285Sobrien return (ENOMEM); 134590285Sobrien } 134618334Speter hook2->hk_refs = 1; /* start with a reference for us. */ 134718334Speter hook2->hk_flags = HK_INVALID; 134818334Speter hook2->hk_peer = hook; /* Link the two together */ 134918334Speter hook->hk_peer = hook2; 1350169699Skan NG_HOOK_REF(hook); /* Add a ref for the peer to each*/ 1351169699Skan NG_HOOK_REF(hook2); 1352169699Skan hook2->hk_node = &ng_deadnode; 1353117404Skan strncpy(NG_HOOK_NAME(hook2), name2, NG_HOOKLEN); 1354117404Skan 1355117404Skan /* 1356117404Skan * Queue the function above. 1357117404Skan * Procesing continues in that function in the lock context of 135818334Speter * the other node. 135918334Speter */ 136018334Speter ng_send_fn(node2, hook2, &ng_con_part2, NULL, 0); 136118334Speter 136218334Speter NG_HOOK_UNREF(hook); /* Let each hook go if it wants to */ 136318334Speter NG_HOOK_UNREF(hook2); 136418334Speter return (0); 136518334Speter} 136618334Speter 136750605Sobrien/* 136850605Sobrien * Make a peer and connect. 136950605Sobrien * We assume that the local node is locked. 137090285Sobrien * The new node probably doesn't need a lock until 137190285Sobrien * it has a hook, because it cannot really have any work until then, 137250605Sobrien * but we should think about it a bit more. 137390285Sobrien * 137490285Sobrien * The problem may come if the other node also fires up 137518334Speter * some hardware or a timer or some other source of activation, 137652557Sobrien * also it may already get a command msg via it's ID. 137752557Sobrien * 137852557Sobrien * We could use the same method as ng_con_nodes() but we'd have 137952557Sobrien * to add ability to remove the node when failing. (Not hard, just 138052557Sobrien * make arg1 point to the node to remove). 138152557Sobrien * Unless of course we just ignore failure to connect and leave 138252557Sobrien * an unconnected node? 138352557Sobrien */ 138452557Sobrienstatic int 138552557Sobrienng_mkpeer(node_p node, const char *name, const char *name2, char *type) 138652557Sobrien{ 138752557Sobrien node_p node2; 138852557Sobrien hook_p hook1; 138952557Sobrien hook_p hook2; 139052557Sobrien int error; 139152557Sobrien 139290285Sobrien if ((error = ng_make_node(type, &node2))) { 139390285Sobrien return (error); 139452557Sobrien } 139552557Sobrien 139652557Sobrien if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */ 139790285Sobrien ng_rmnode(node2, NULL, NULL, 0); 139852557Sobrien return (error); 139952557Sobrien } 140090285Sobrien 140190285Sobrien if ((error = ng_add_hook(node2, name2, &hook2))) { 140252557Sobrien ng_rmnode(node2, NULL, NULL, 0); 140352557Sobrien ng_destroy_hook(hook1); 140490285Sobrien NG_HOOK_UNREF(hook1); 140552557Sobrien return (error); 140690285Sobrien } 140790285Sobrien 140852557Sobrien /* 140918334Speter * Actually link the two hooks together. 141052557Sobrien */ 141190285Sobrien hook1->hk_peer = hook2; 141290285Sobrien hook2->hk_peer = hook1; 141352557Sobrien 141490285Sobrien /* Each hook is referenced by the other */ 141590285Sobrien NG_HOOK_REF(hook1); 141690285Sobrien NG_HOOK_REF(hook2); 141790285Sobrien 141890285Sobrien /* Give each node the opportunity to veto the pending connection */ 141990285Sobrien if (hook1->hk_node->nd_type->connect) { 142090285Sobrien error = (*hook1->hk_node->nd_type->connect) (hook1); 142118334Speter } 142218334Speter 142390285Sobrien if ((error == 0) && hook2->hk_node->nd_type->connect) { 142418334Speter error = (*hook2->hk_node->nd_type->connect) (hook2); 142518334Speter 142618334Speter } 142718334Speter 142890285Sobrien /* 142918334Speter * drop the references we were holding on the two hooks. 143018334Speter */ 143118334Speter if (error) { 143218334Speter ng_destroy_hook(hook2); /* also zaps hook1 */ 143318334Speter ng_rmnode(node2, NULL, NULL, 0); 143418334Speter } else { 143518334Speter /* As a last act, allow the hooks to be used */ 143618334Speter hook1->hk_flags &= ~HK_INVALID; 143718334Speter hook2->hk_flags &= ~HK_INVALID; 143818334Speter } 143918334Speter NG_HOOK_UNREF(hook1); 144018334Speter NG_HOOK_UNREF(hook2); 144118334Speter return (error); 144290285Sobrien} 144390285Sobrien 144418334Speter/************************************************************************ 144518334Speter Utility routines to send self messages 144618334Speter************************************************************************/ 1447169699Skan 144818334Speter/* Shut this node down as soon as everyone is clear of it */ 144918334Speter/* Should add arg "immediatly" to jump the queue */ 145018334Speterint 145118334Speterng_rmnode_self(node_p node) 145218334Speter{ 145318334Speter int error; 145418334Speter 145518334Speter if (node == &ng_deadnode) 145618334Speter return (0); 145718334Speter node->nd_flags |= NG_INVALID; 145818334Speter if (node->nd_flags & NG_CLOSING) 145990285Sobrien return (0); 146018334Speter 146118334Speter error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0); 146218334Speter return (error); 146318334Speter} 146418334Speter 146518334Speterstatic void 146618334Speterng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2) 146790285Sobrien{ 146818334Speter ng_destroy_hook(hook); 146918334Speter return ; 147018334Speter} 147118334Speter 147218334Speterint 147318334Speterng_rmhook_self(hook_p hook) 147418334Speter{ 147518334Speter int error; 147618334Speter node_p node = NG_HOOK_NODE(hook); 147718334Speter 147818334Speter if (node == &ng_deadnode) 147918334Speter return (0); 148090285Sobrien 148118334Speter error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0); 148218334Speter return (error); 148390285Sobrien} 148418334Speter 148590285Sobrien/*********************************************************************** 148690285Sobrien * Parse and verify a string of the form: <NODE:><PATH> 148790285Sobrien * 148890285Sobrien * Such a string can refer to a specific node or a specific hook 148918334Speter * on a specific node, depending on how you look at it. In the 149018334Speter * latter case, the PATH component must not end in a dot. 149118334Speter * 149218334Speter * Both <NODE:> and <PATH> are optional. The <PATH> is a string 149318334Speter * of hook names separated by dots. This breaks out the original 149490285Sobrien * string, setting *nodep to "NODE" (or NULL if none) and *pathp 1495169699Skan * to "PATH" (or NULL if degenerate). Also, *hookp will point to 149618334Speter * the final hook component of <PATH>, if any, otherwise NULL. 149718334Speter * 149890285Sobrien * This returns -1 if the path is malformed. The char ** are optional. 149918334Speter ***********************************************************************/ 150018334Speterint 150118334Speterng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 150218334Speter{ 150318334Speter char *node, *path, *hook; 150418334Speter int k; 150518334Speter 150618334Speter /* 150718334Speter * Extract absolute NODE, if any 150818334Speter */ 150918334Speter for (path = addr; *path && *path != ':'; path++); 151018334Speter if (*path) { 151118334Speter node = addr; /* Here's the NODE */ 151218334Speter *path++ = '\0'; /* Here's the PATH */ 1513169699Skan 151418334Speter /* Node name must not be empty */ 151518334Speter if (!*node) 151618334Speter return -1; 151790285Sobrien 151818334Speter /* A name of "." is OK; otherwise '.' not allowed */ 151990285Sobrien if (strcmp(node, ".") != 0) { 152090285Sobrien for (k = 0; node[k]; k++) 152190285Sobrien if (node[k] == '.') 152218334Speter return -1; 152318334Speter } 1524169699Skan } else { 152518334Speter node = NULL; /* No absolute NODE */ 152618334Speter path = addr; /* Here's the PATH */ 1527169699Skan } 1528169699Skan 1529169699Skan /* Snoop for illegal characters in PATH */ 1530169699Skan for (k = 0; path[k]; k++) 1531169699Skan if (path[k] == ':') 153218334Speter return -1; 153318334Speter 1534169699Skan /* Check for no repeated dots in PATH */ 1535169699Skan for (k = 0; path[k]; k++) 153618334Speter if (path[k] == '.' && path[k + 1] == '.') 153718334Speter return -1; 153818334Speter 153918334Speter /* Remove extra (degenerate) dots from beginning or end of PATH */ 154018334Speter if (path[0] == '.') 154118334Speter path++; 154218334Speter if (*path && path[strlen(path) - 1] == '.') 1543169699Skan path[strlen(path) - 1] = 0; 1544169699Skan 154518334Speter /* If PATH has a dot, then we're not talking about a hook */ 154618334Speter if (*path) { 154718334Speter for (hook = path, k = 0; path[k]; k++) 154818334Speter if (path[k] == '.') { 154918334Speter hook = NULL; 155018334Speter break; 155118334Speter } 155218334Speter } else 155318334Speter path = hook = NULL; 155418334Speter 155518334Speter /* Done */ 155690285Sobrien if (nodep) 155790285Sobrien *nodep = node; 155818334Speter if (pathp) 155970639Sobrien *pathp = path; 156018334Speter if (hookp) 156170639Sobrien *hookp = hook; 1562169699Skan return (0); 1563169699Skan} 156470639Sobrien 156570639Sobrien/* 156670639Sobrien * Given a path, which may be absolute or relative, and a starting node, 156770639Sobrien * return the destination node. 156870639Sobrien */ 156970639Sobrienint 157070639Sobrienng_path2noderef(node_p here, const char *address, 1571132727Skan node_p *destp, hook_p *lasthook) 1572132727Skan{ 1573169699Skan char fullpath[NG_PATHLEN + 1]; 1574132727Skan char *nodename, *path, pbuf[2]; 1575132727Skan node_p node, oldnode; 157670639Sobrien char *cp; 157790285Sobrien hook_p hook = NULL; 157870639Sobrien 157970639Sobrien /* Initialize */ 158018334Speter if (destp == NULL) { 158118334Speter TRAP_ERROR(); 158218334Speter return EINVAL; 158318334Speter } 158418334Speter *destp = NULL; 158518334Speter 158618334Speter /* Make a writable copy of address for ng_path_parse() */ 158718334Speter strncpy(fullpath, address, sizeof(fullpath) - 1); 158818334Speter fullpath[sizeof(fullpath) - 1] = '\0'; 158918334Speter 159018334Speter /* Parse out node and sequence of hooks */ 159118334Speter if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 159218334Speter TRAP_ERROR(); 159318334Speter return EINVAL; 159418334Speter } 159518334Speter if (path == NULL) { 1596132727Skan pbuf[0] = '.'; /* Needs to be writable */ 159718334Speter pbuf[1] = '\0'; 159818334Speter path = pbuf; 159918334Speter } 160090285Sobrien 160118334Speter /* 160218334Speter * For an absolute address, jump to the starting node. 160318334Speter * Note that this holds a reference on the node for us. 160418334Speter * Don't forget to drop the reference if we don't need it. 160518334Speter */ 160618334Speter if (nodename) { 1607117404Skan node = ng_name2noderef(here, nodename); 1608117404Skan if (node == NULL) { 1609117404Skan TRAP_ERROR(); 1610117404Skan return (ENOENT); 1611117404Skan } 1612117404Skan } else { 1613132727Skan if (here == NULL) { 1614117404Skan TRAP_ERROR(); 1615117404Skan return (EINVAL); 1616117404Skan } 1617117404Skan node = here; 1618117404Skan NG_NODE_REF(node); 1619117404Skan } 1620117404Skan 1621117404Skan /* 1622117404Skan * Now follow the sequence of hooks 1623117404Skan * XXX 162418334Speter * We actually cannot guarantee that the sequence 162518334Speter * is not being demolished as we crawl along it 162618334Speter * without extra-ordinary locking etc. 162718334Speter * So this is a bit dodgy to say the least. 162818334Speter * We can probably hold up some things by holding 1629132727Skan * the nodelist mutex for the time of this 163018334Speter * crawl if we wanted.. At least that way we wouldn't have to 163118334Speter * worry about the nodes dissappearing, but the hooks would still 163218334Speter * be a problem. 163318334Speter */ 163418334Speter for (cp = path; node != NULL && *cp != '\0'; ) { 163518334Speter char *segment; 163618334Speter 163718334Speter /* 163852557Sobrien * Break out the next path segment. Replace the dot we just 163952557Sobrien * found with a NUL; "cp" points to the next segment (or the 164052557Sobrien * NUL at the end). 1641117404Skan */ 164252557Sobrien for (segment = cp; *cp != '\0'; cp++) { 1643132727Skan if (*cp == '.') { 164450605Sobrien *cp++ = '\0'; 164550605Sobrien break; 164652557Sobrien } 164752557Sobrien } 164850605Sobrien 164990285Sobrien /* Empty segment */ 165050605Sobrien if (*segment == '\0') 165150605Sobrien continue; 165252557Sobrien 165352557Sobrien /* We have a segment, so look for a hook by that name */ 165452557Sobrien hook = ng_findhook(node, segment); 165552557Sobrien 165652557Sobrien /* Can't get there from here... */ 165752557Sobrien if (hook == NULL 165852557Sobrien || NG_HOOK_PEER(hook) == NULL 165950605Sobrien || NG_HOOK_NOT_VALID(hook) 166052557Sobrien || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) { 166152557Sobrien TRAP_ERROR(); 166252557Sobrien NG_NODE_UNREF(node); 166352557Sobrien#if 0 166452557Sobrien printf("hooknotvalid %s %s %d %d %d %d ", 166552557Sobrien path, 166652557Sobrien segment, 166752557Sobrien hook == NULL, 166890285Sobrien NG_HOOK_PEER(hook) == NULL, 166990285Sobrien NG_HOOK_NOT_VALID(hook), 167052557Sobrien NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))); 167152557Sobrien#endif 167252557Sobrien return (ENOENT); 167352557Sobrien } 167450605Sobrien 167550605Sobrien /* 167618334Speter * Hop on over to the next node 167718334Speter * XXX 167818334Speter * Big race conditions here as hooks and nodes go away 167918334Speter * *** Idea.. store an ng_ID_t in each hook and use that 168018334Speter * instead of the direct hook in this crawl? 168118334Speter */ 168218334Speter oldnode = node; 168318334Speter if ((node = NG_PEER_NODE(hook))) 168418334Speter NG_NODE_REF(node); /* XXX RACE */ 168518334Speter NG_NODE_UNREF(oldnode); /* XXX another race */ 168618334Speter if (NG_NODE_NOT_VALID(node)) { 168718334Speter NG_NODE_UNREF(node); /* XXX more races */ 168818334Speter node = NULL; 1689132727Skan } 169018334Speter } 169118334Speter 169218334Speter /* If node somehow missing, fail here (probably this is not needed) */ 169318334Speter if (node == NULL) { 169418334Speter TRAP_ERROR(); 169518334Speter return (ENXIO); 169618334Speter } 169718334Speter 169818334Speter /* Done */ 169918334Speter *destp = node; 170090285Sobrien if (lasthook != NULL) 170118334Speter *lasthook = (hook ? NG_HOOK_PEER(hook) : NULL); 170218334Speter return (0); 170318334Speter} 170418334Speter 170518334Speter/***************************************************************\ 170618334Speter* Input queue handling. 170790285Sobrien* All activities are submitted to the node via the input queue 170818334Speter* which implements a multiple-reader/single-writer gate. 170918334Speter* Items which cannot be handled immeditly are queued. 171018334Speter* 171118334Speter* read-write queue locking inline functions * 171290285Sobrien\***************************************************************/ 171318334Speter 171418334Speterstatic __inline item_p ng_dequeue(struct ng_queue * ngq); 171518334Speterstatic __inline item_p ng_acquire_read(struct ng_queue * ngq, 171690285Sobrien item_p item); 171718334Speterstatic __inline item_p ng_acquire_write(struct ng_queue * ngq, 171818334Speter item_p item); 171990285Sobrienstatic __inline void ng_leave_read(struct ng_queue * ngq); 172090285Sobrienstatic __inline void ng_leave_write(struct ng_queue * ngq); 172190285Sobrienstatic __inline void ng_queue_rw(struct ng_queue * ngq, 172290285Sobrien item_p item, int rw); 172390285Sobrien 172490285Sobrien/* 172590285Sobrien * Definition of the bits fields in the ng_queue flag word. 172690285Sobrien * Defined here rather than in netgraph.h because no-one should fiddle 172790285Sobrien * with them. 172890285Sobrien * 172918334Speter * The ordering here may be important! don't shuffle these. 173018334Speter */ 173118334Speter/*- 173290285Sobrien Safety Barrier--------+ (adjustable to suit taste) (not used yet) 173318334Speter | 173490285Sobrien V 173590285Sobrien+-------+-------+-------+-------+-------+-------+-------+-------+ 173690285Sobrien| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 173790285Sobrien| |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |R|A|W| 173890285Sobrien| | | | | | | | | | | | | | | | | | | | | | | | | | | | | |P|W|P| 173990285Sobrien+-------+-------+-------+-------+-------+-------+-------+-------+ 174090285Sobrien\___________________________ ____________________________/ | | | 174190285Sobrien V | | | 174218334Speter [active reader count] | | | 174318334Speter | | | 174418334Speter Read Pending ------------------------------------+ | | 174590285Sobrien | | 174690285Sobrien Active Writer -------------------------------------+ | 174790285Sobrien | 174890285Sobrien Write Pending ---------------------------------------+ 174918334Speter 175050605Sobrien 175190285Sobrien*/ 175290285Sobrien#define WRITE_PENDING 0x00000001 175390285Sobrien#define WRITER_ACTIVE 0x00000002 175490285Sobrien#define READ_PENDING 0x00000004 175590285Sobrien#define READER_INCREMENT 0x00000008 175690285Sobrien#define READER_MASK 0xfffffff0 /* Not valid if WRITER_ACTIVE is set */ 175718334Speter#define SAFETY_BARRIER 0x00100000 /* 64K items queued should be enough */ 175818334Speter 175918334Speter/* Defines of more elaborate states on the queue */ 176090285Sobrien/* Mask of bits a read cares about */ 176190285Sobrien#define NGQ_RMASK (WRITE_PENDING|WRITER_ACTIVE|READ_PENDING) 176218334Speter 176318334Speter/* Mask of bits a write cares about */ 176418334Speter#define NGQ_WMASK (NGQ_RMASK|READER_MASK) 176518334Speter 176618334Speter/* tests to decide if we could get a read or write off the queue */ 176718334Speter#define CAN_GET_READ(flag) ((flag & NGQ_RMASK) == READ_PENDING) 1768169699Skan#define CAN_GET_WRITE(flag) ((flag & NGQ_WMASK) == WRITE_PENDING) 176990285Sobrien 177090285Sobrien/* Is there a chance of getting ANY work off the queue? */ 1771107605Sobrien#define CAN_GET_WORK(flag) (CAN_GET_READ(flag) || CAN_GET_WRITE(flag)) 1772107605Sobrien 177390285Sobrien/* 177450605Sobrien * Taking into account the current state of the queue and node, possibly take 177518334Speter * the next entry off the queue and return it. Return NULL if there was 177618334Speter * nothing we could return, either because there really was nothing there, or 177790285Sobrien * because the node was in a state where it cannot yet process the next item 177890285Sobrien * on the queue. 177918334Speter * 178018334Speter * This MUST MUST MUST be called with the mutex held. 178118334Speter */ 178218334Speterstatic __inline item_p 178390285Sobrienng_dequeue(struct ng_queue *ngq) 178490285Sobrien{ 178590285Sobrien item_p item; 178618334Speter u_int add_arg; 178790285Sobrien 178818334Speter if (CAN_GET_READ(ngq->q_flags)) { 178990285Sobrien /* 179050605Sobrien * Head of queue is a reader and we have no write active. 179190285Sobrien * We don't care how many readers are already active. 179218334Speter * Adjust the flags for the item we are about to dequeue. 179390285Sobrien * Add the correct increment for the reader count as well. 179490285Sobrien */ 179590285Sobrien add_arg = (READER_INCREMENT - READ_PENDING); 179690285Sobrien } else if (CAN_GET_WRITE(ngq->q_flags)) { 179718334Speter /* 179818334Speter * There is a pending write, no readers and no active writer. 179918334Speter * This means we can go ahead with the pending writer. Note 180018334Speter * the fact that we now have a writer, ready for when we take 180190285Sobrien * it off the queue. 180290285Sobrien * 180390285Sobrien * We don't need to worry about a possible collision with the 180418334Speter * fasttrack reader. 180550605Sobrien * 180690285Sobrien * The fasttrack thread may take a long time to discover that we 180790285Sobrien * are running so we would have an inconsistent state in the 180890285Sobrien * flags for a while. Since we ignore the reader count 180918334Speter * entirely when the WRITER_ACTIVE flag is set, this should 181018334Speter * not matter (in fact it is defined that way). If it tests 181118334Speter * the flag before this operation, the WRITE_PENDING flag 181218334Speter * will make it fail, and if it tests it later, the 181318334Speter * WRITER_ACTIVE flag will do the same. If it is SO slow that 181418334Speter * we have actually completed the operation, and neither flag 181518334Speter * is set (nor the READ_PENDING) by the time that it tests 181618334Speter * the flags, then it is actually ok for it to continue. If 181718334Speter * it completes and we've finished and the read pending is 181818334Speter * set it still fails. 181918334Speter * 182018334Speter * So we can just ignore it, as long as we can ensure that the 182118334Speter * transition from WRITE_PENDING state to the WRITER_ACTIVE 182218334Speter * state is atomic. 182318334Speter * 182418334Speter * After failing, first it will be held back by the mutex, then 182518334Speter * when it can proceed, it will queue its request, then it 182618334Speter * would arrive at this function. Usually it will have to 182790285Sobrien * leave empty handed because the ACTIVE WRITER bit will be 182890285Sobrien * set. 182990285Sobrien * 183018334Speter * Adjust the flags for the item we are about to dequeue 183118334Speter * and for the new active writer. 183218334Speter */ 183318334Speter add_arg = (WRITER_ACTIVE - WRITE_PENDING); 183418334Speter /* 183518334Speter * We want to write "active writer, no readers " Now go make 183618334Speter * it true. In fact there may be a number in the readers 1837169699Skan * count but we know it is not true and will be fixed soon. 183818334Speter * We will fix the flags for the next pending entry in a 183990285Sobrien * moment. 184018334Speter */ 184190285Sobrien } else { 184290285Sobrien /* 184318334Speter * We can't dequeue anything.. return and say so. Probably we 1844169699Skan * have a write pending and the readers count is non zero. If 1845169699Skan * we got here because a reader hit us just at the wrong 184618334Speter * moment with the fasttrack code, and put us in a strange 184718334Speter * state, then it will be through in just a moment, (as soon 184890285Sobrien * as we release the mutex) and keep things moving. 184990285Sobrien * Make sure we remove ourselves from the work queue. 185090285Sobrien */ 185190285Sobrien ng_worklist_remove(ngq->q_node); 185290285Sobrien return (0); 185318334Speter } 185490285Sobrien 185518334Speter /* 1856169699Skan * Now we dequeue the request (whatever it may be) and correct the 1857169699Skan * pending flags and the next and last pointers. 1858169699Skan */ 1859169699Skan item = ngq->queue; 1860169699Skan ngq->queue = item->el_next; 1861169699Skan if (ngq->last == &(item->el_next)) { 186218334Speter /* 186390285Sobrien * that was the last entry in the queue so set the 'last 186490285Sobrien * pointer up correctly and make sure the pending flags are 186550605Sobrien * clear. 186618334Speter */ 186718334Speter ngq->last = &(ngq->queue); 186818334Speter /* 186918334Speter * Whatever flag was set will be cleared and 187018334Speter * the new acive field will be set by the add as well, 187118334Speter * so we don't need to change add_arg. 187218334Speter * But we know we don't need to be on the work list. 187318334Speter */ 187418334Speter atomic_add_long(&ngq->q_flags, add_arg); 187518334Speter ng_worklist_remove(ngq->q_node); 187618334Speter } else { 187718334Speter /* 187818334Speter * Since there is something on the queue, note what it is 187918334Speter * in the flags word. 188090285Sobrien */ 188118334Speter if ((ngq->queue->el_flags & NGQF_RW) == NGQF_READER) { 188218334Speter add_arg += READ_PENDING; 188350605Sobrien } else { 188418334Speter add_arg += WRITE_PENDING; 1885117404Skan } 188650605Sobrien atomic_add_long(&ngq->q_flags, add_arg); 188750605Sobrien /* 188850605Sobrien * If we see more doable work, make sure we are 188950605Sobrien * on the work queue. 189018334Speter */ 1891132727Skan if (CAN_GET_WORK(ngq->q_flags)) { 1892132727Skan ng_setisr(ngq->q_node); 1893132727Skan } 189418334Speter } 189518334Speter /* 189618334Speter * We have successfully cleared the old pending flag, set the new one 189718334Speter * if it is needed, and incremented the appropriate active field. 189818334Speter * (all in one atomic addition.. ) 189918334Speter */ 190018334Speter return (item); 190118334Speter} 190218334Speter 190318334Speter/* 190418334Speter * Queue a packet to be picked up by someone else. 190518334Speter * We really don't care who, but we can't or don't want to hang around 190618334Speter * to process it ourselves. We are probably an interrupt routine.. 190718334Speter * 1 = writer, 0 = reader 190890285Sobrien */ 190990285Sobrien#define NGQRW_R 0 191090285Sobrien#define NGQRW_W 1 191118334Speterstatic __inline void 191218334Speterng_queue_rw(struct ng_queue * ngq, item_p item, int rw) 191318334Speter{ 1914169699Skan item->el_next = NULL; /* maybe not needed */ 191590285Sobrien *ngq->last = item; 191690285Sobrien /* 191790285Sobrien * If it was the first item in the queue then we need to 191890285Sobrien * set the last pointer and the type flags. 191990285Sobrien */ 192018334Speter if (ngq->last == &(ngq->queue)) { 192118334Speter /* 192218334Speter * When called with constants for rw, the optimiser will 192318334Speter * remove the unneeded branch below. 1924169699Skan */ 192590285Sobrien if (rw == NGQRW_W) { 192690285Sobrien atomic_add_long(&ngq->q_flags, WRITE_PENDING); 192790285Sobrien } else { 192890285Sobrien atomic_add_long(&ngq->q_flags, READ_PENDING); 192990285Sobrien } 193018334Speter } 193118334Speter ngq->last = &(item->el_next); 193218334Speter} 193318334Speter 193418334Speter 1935169699Skan/* 1936169699Skan * This function 'cheats' in that it first tries to 'grab' the use of the 1937169699Skan * node, without going through the mutex. We can do this becasue of the 1938169699Skan * semantics of the lock. The semantics include a clause that says that the 1939169699Skan * value of the readers count is invalid if the WRITER_ACTIVE flag is set. It 194018334Speter * also says that the WRITER_ACTIVE flag cannot be set if the readers count 194118334Speter * is not zero. Note that this talks about what is valid to SET the 1942169699Skan * WRITER_ACTIVE flag, because from the moment it is set, the value if the 194318334Speter * reader count is immaterial, and not valid. The two 'pending' flags have a 194418334Speter * similar effect, in that If they are orthogonal to the two active fields in 194590285Sobrien * how they are set, but if either is set, the attempted 'grab' need to be 1946169699Skan * backed out because there is earlier work, and we maintain ordering in the 194718334Speter * queue. The result of this is that the reader request can try obtain use of 194818334Speter * the node with only a single atomic addition, and without any of the mutex 194918334Speter * overhead. If this fails the operation degenerates to the same as for other 195018334Speter * cases. 195118334Speter * 195218334Speter */ 195318334Speterstatic __inline item_p 195418334Speterng_acquire_read(struct ng_queue *ngq, item_p item) 195518334Speter{ 195618334Speter 195718334Speter /* ######### Hack alert ######### */ 195818334Speter atomic_add_long(&ngq->q_flags, READER_INCREMENT); 195918334Speter if ((ngq->q_flags & NGQ_RMASK) == 0) { 196018334Speter /* Successfully grabbed node */ 196118334Speter return (item); 196296288Sobrien } 196318334Speter /* undo the damage if we didn't succeed */ 196418334Speter atomic_subtract_long(&ngq->q_flags, READER_INCREMENT); 196518334Speter 196690285Sobrien /* ######### End Hack alert ######### */ 196790285Sobrien mtx_lock_spin((&ngq->q_mtx)); 196818334Speter /* 196918334Speter * Try again. Another processor (or interrupt for that matter) may 197018334Speter * have removed the last queued item that was stopping us from 197118334Speter * running, between the previous test, and the moment that we took 197218334Speter * the mutex. (Or maybe a writer completed.) 197318334Speter */ 197418334Speter if ((ngq->q_flags & NGQ_RMASK) == 0) { 1975169699Skan atomic_add_long(&ngq->q_flags, READER_INCREMENT); 197618334Speter mtx_unlock_spin((&ngq->q_mtx)); 197718334Speter return (item); 197850605Sobrien } 197918334Speter 198018334Speter /* 198118334Speter * and queue the request for later. 198218334Speter */ 198318334Speter item->el_flags |= NGQF_READER; 198418334Speter ng_queue_rw(ngq, item, NGQRW_R); 198518334Speter 198618334Speter /* 198718334Speter * Ok, so that's the item successfully queued for later. So now we 198818334Speter * see if we can dequeue something to run instead. 198950605Sobrien */ 199050605Sobrien item = ng_dequeue(ngq); 199118334Speter mtx_unlock_spin(&(ngq->q_mtx)); 1992169699Skan return (item); 199318334Speter} 199418334Speter 199518334Speterstatic __inline item_p 199618334Speterng_acquire_write(struct ng_queue *ngq, item_p item) 199718334Speter{ 199818334Speterrestart: 199918334Speter mtx_lock_spin(&(ngq->q_mtx)); 200018334Speter /* 200118334Speter * If there are no readers, no writer, and no pending packets, then 200218334Speter * we can just go ahead. In all other situations we need to queue the 200318334Speter * request 2004169699Skan */ 2005169699Skan if ((ngq->q_flags & NGQ_WMASK) == 0) { 2006169699Skan atomic_add_long(&ngq->q_flags, WRITER_ACTIVE); 2007169699Skan mtx_unlock_spin((&ngq->q_mtx)); 2008169699Skan if (ngq->q_flags & READER_MASK) { 2009169699Skan /* Collision with fast-track reader */ 2010169699Skan atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE); 2011169699Skan goto restart; 2012169699Skan } 2013169699Skan return (item); 2014169699Skan } 201518334Speter 201690285Sobrien /* 2017169699Skan * and queue the request for later. 201818334Speter */ 201990285Sobrien item->el_flags &= ~NGQF_RW; 202018334Speter ng_queue_rw(ngq, item, NGQRW_W); 202150605Sobrien 202250605Sobrien /* 202350605Sobrien * Ok, so that's the item successfully queued for later. So now we 202450605Sobrien * see if we can dequeue something to run instead. 202518334Speter */ 202690285Sobrien item = ng_dequeue(ngq); 202790285Sobrien mtx_unlock_spin(&(ngq->q_mtx)); 202818334Speter return (item); 202918334Speter} 203018334Speter 203118334Speterstatic __inline void 203218334Speterng_leave_read(struct ng_queue *ngq) 203318334Speter{ 203418334Speter atomic_subtract_long(&ngq->q_flags, READER_INCREMENT); 203518334Speter} 203618334Speter 203718334Speterstatic __inline void 203818334Speterng_leave_write(struct ng_queue *ngq) 203990285Sobrien{ 2040169699Skan atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE); 204118334Speter} 204218334Speter 204350605Sobrienstatic void 204418334Speterng_flush_input_queue(struct ng_queue * ngq) 204518334Speter{ 204618334Speter item_p item; 204718334Speter u_int add_arg; 204818334Speter mtx_lock_spin(&ngq->q_mtx); 204918334Speter for (;;) { 205018334Speter /* Now take a look at what's on the queue */ 205118334Speter if (ngq->q_flags & READ_PENDING) { 205218334Speter add_arg = -READ_PENDING; 205318334Speter } else if (ngq->q_flags & WRITE_PENDING) { 205418334Speter add_arg = -WRITE_PENDING; 205518334Speter } else { 205618334Speter break; 205770639Sobrien } 2058132727Skan 205918334Speter item = ngq->queue; 206018334Speter ngq->queue = item->el_next; 206118334Speter if (ngq->last == &(item->el_next)) { 206218334Speter ngq->last = &(ngq->queue); 206318334Speter } else { 206418334Speter if ((ngq->queue->el_flags & NGQF_RW) == NGQF_READER) { 206518334Speter add_arg += READ_PENDING; 206618334Speter } else { 206718334Speter add_arg += WRITE_PENDING; 206818334Speter } 206918334Speter } 207018334Speter atomic_add_long(&ngq->q_flags, add_arg); 207118334Speter 207218334Speter mtx_lock_spin(&ngq->q_mtx); 207318334Speter NG_FREE_ITEM(item); 207418334Speter mtx_unlock_spin(&ngq->q_mtx); 2075132727Skan } 207618334Speter /* 207718334Speter * Take us off the work queue if we are there. 207818334Speter * We definatly have no work to be done. 207990285Sobrien */ 208090285Sobrien ng_worklist_remove(ngq->q_node); 208118334Speter mtx_unlock_spin(&ngq->q_mtx); 208218334Speter} 2083169699Skan 208418334Speter/*********************************************************************** 208590285Sobrien* Externally visible method for sending or queueing messages or data. 208690285Sobrien***********************************************************************/ 208718334Speter 208818334Speter/* 2089169699Skan * The module code should have filled out the item correctly by this stage: 209018334Speter * Common: 209118334Speter * reference to destination node. 209218334Speter * Reference to destination rcv hook if relevant. 209318334Speter * Data: 209418334Speter * pointer to mbuf 209590285Sobrien * pointer to metadata 209690285Sobrien * Control_Message: 209718334Speter * pointer to msg. 209818334Speter * ID of original sender node. (return address) 209918334Speter * Function: 210018334Speter * Function pointer 210118334Speter * void * argument 210218334Speter * integer argument 210318334Speter * 210418334Speter * The nodes have several routines and macros to help with this task: 210518334Speter */ 210618334Speter 210718334Speterint 210818334Speterng_snd_item(item_p item, int queue) 210918334Speter{ 2110132727Skan hook_p hook = NGI_HOOK(item); 211118334Speter node_p node = NGI_NODE(item); 211218334Speter int rw; 211318334Speter int error = 0, ierror; 211418334Speter item_p oitem; 211518334Speter struct ng_queue * ngq = &node->nd_input_queue; 211618334Speter 211718334Speter#ifdef NETGRAPH_DEBUG 211818334Speter _ngi_check(item, __FILE__, __LINE__); 211918334Speter#endif 212018334Speter 212118334Speter if (item == NULL) { 212218334Speter TRAP_ERROR(); 212318334Speter return (EINVAL); /* failed to get queue element */ 212418334Speter } 212518334Speter if (node == NULL) { 212618334Speter NG_FREE_ITEM(item); 212718334Speter TRAP_ERROR(); 212818334Speter return (EINVAL); /* No address */ 212918334Speter } 213018334Speter switch(item->el_flags & NGQF_TYPE) { 213118334Speter case NGQF_DATA: 213218334Speter /* 213318334Speter * DATA MESSAGE 213418334Speter * Delivered to a node via a non-optional hook. 2135132727Skan * Both should be present in the item even though 213618334Speter * the node is derivable from the hook. 213790285Sobrien * References are held on both by the item. 213890285Sobrien */ 213990285Sobrien CHECK_DATA_MBUF(NGI_M(item)); 214018334Speter if (hook == NULL) { 214190285Sobrien NG_FREE_ITEM(item); 214218334Speter TRAP_ERROR(); 214318334Speter return(EINVAL); 2144169699Skan } 2145169699Skan if ((NG_HOOK_NOT_VALID(hook)) 2146169699Skan || (NG_NODE_NOT_VALID(NG_HOOK_NODE(hook)))) { 214718334Speter NG_FREE_ITEM(item); 214890285Sobrien return (ENOTCONN); 214918334Speter } 215018334Speter if ((hook->hk_flags & HK_QUEUE)) { 215118334Speter queue = 1; 215218334Speter } 215318334Speter /* By default data is a reader in the locking scheme */ 215418334Speter item->el_flags |= NGQF_READER; 215590285Sobrien rw = NGQRW_R; 215690285Sobrien break; 215790285Sobrien case NGQF_MESG: 215890285Sobrien /* 215918334Speter * CONTROL MESSAGE 216018334Speter * Delivered to a node. 216118334Speter * Hook is optional. 216218334Speter * References are held by the item on the node and 216318334Speter * the hook if it is present. 216418334Speter */ 216518334Speter if (hook && (hook->hk_flags & HK_QUEUE)) { 216618334Speter queue = 1; 216718334Speter } 216890285Sobrien /* Data messages count as writers unles explicitly exempted */ 216990285Sobrien if (NGI_MSG(item)->header.cmd & NGM_READONLY) { 217090285Sobrien item->el_flags |= NGQF_READER; 217190285Sobrien rw = NGQRW_R; 217218334Speter } else { 217318334Speter item->el_flags &= ~NGQF_RW; 217418334Speter rw = NGQRW_W; 217518334Speter } 217618334Speter break; 2177146906Skan case NGQF_FN: 2178146906Skan item->el_flags &= ~NGQF_RW; 2179146906Skan rw = NGQRW_W; 218018334Speter break; 2181146906Skan default: 218218334Speter NG_FREE_ITEM(item); 2183169699Skan TRAP_ERROR(); 218418334Speter return (EINVAL); 2185146906Skan } 218618334Speter /* 2187169699Skan * If the node specifies single threading, force writer semantics 218818334Speter * Similarly the node may say one hook always produces writers. 218918334Speter * These are over-rides. 219018334Speter */ 219118334Speter if ((node->nd_flags & NG_FORCE_WRITER) 219218334Speter || (hook && (hook->hk_flags & HK_FORCE_WRITER))) { 219318334Speter rw = NGQRW_W; 2194117404Skan item->el_flags &= ~NGQF_READER; 219518334Speter } 219690285Sobrien if (queue) { 219718334Speter /* Put it on the queue for that node*/ 219818334Speter#ifdef NETGRAPH_DEBUG 219918334Speter _ngi_check(item, __FILE__, __LINE__); 2200117404Skan#endif 220118334Speter mtx_lock_spin(&(ngq->q_mtx)); 220218334Speter ng_queue_rw(ngq, item, rw); 220318334Speter /* 220490285Sobrien * If there are active elements then we can rely on 220590285Sobrien * them. if not we should not rely on another packet 220618334Speter * coming here by another path, 220718334Speter * so it is best to put us in the netisr list. 220818334Speter * We can take the worklist lock with the node locked 220918334Speter * BUT NOT THE REVERSE! 2210169699Skan */ 2211169699Skan if (CAN_GET_WORK(ngq->q_flags)) { 221218334Speter ng_setisr(node); 221318334Speter } 221418334Speter mtx_unlock_spin(&(ngq->q_mtx)); 221518334Speter return (0); 221618334Speter } 221718334Speter /* 221818334Speter * Take a queue item and a node and see if we can apply the item to 2219169699Skan * the node. We may end up getting a different item to apply instead. 2220169699Skan * Will allow for a piggyback reply only in the case where 2221169699Skan * there is no queueing. 2222169699Skan */ 2223169699Skan 2224169699Skan oitem = item; 2225169699Skan /* 2226169699Skan * We already decided how we will be queueud or treated. 2227169699Skan * Try get the appropriate operating permission. 2228169699Skan */ 2229169699Skan if (rw == NGQRW_R) { 2230169699Skan item = ng_acquire_read(ngq, item); 2231169699Skan } else { 2232169699Skan item = ng_acquire_write(ngq, item); 2233169699Skan } 223418334Speter 223518334Speter /* 223618334Speter * May have come back with a different item. 223718334Speter * or maybe none at all. The one we started with will 223818334Speter * have been queued in thises cases. 223918334Speter */ 224018334Speter if (item == NULL) { 224152557Sobrien return (0); 224218334Speter } 224318334Speter 224418334Speter#ifdef NETGRAPH_DEBUG 224518334Speter _ngi_check(item, __FILE__, __LINE__); 224618334Speter#endif 224718334Speter /* 224818334Speter * Take over the reference frm the item. 224918334Speter * Hold it until the called function returns. 225018334Speter */ 225118334Speter NGI_GET_NODE(item, node); /* zaps stored node */ 225218334Speter 225318334Speter ierror = ng_apply_item(node, item); /* drops r/w lock when done */ 225418334Speter 225518334Speter /* only return an error if it was our initial item.. (compat hack) */ 225618334Speter if (oitem == item) { 225718334Speter error = ierror; 225818334Speter } 225918334Speter 226018334Speter /* 226118334Speter * If the node goes away when we remove the reference, 226218334Speter * whatever we just did caused it.. whatever we do, DO NOT 226318334Speter * access the node again! 226418334Speter */ 226518334Speter if (NG_NODE_UNREF(node) == 0) { 226618334Speter return (error); 226752557Sobrien } 226852557Sobrien 226952557Sobrien /* 227052557Sobrien * Now we've handled the packet we brought, (or a friend of it) let's 227152557Sobrien * look for any other packets that may have been queued up. We hold 227252557Sobrien * no locks, so if someone puts something in the queue after 227352557Sobrien * we check that it is empty, it is their problem 227452557Sobrien * to ensure it is processed. If we have the netisr thread cme in here 227552557Sobrien * while we still say we have stuff to do, we may get a boost 227652557Sobrien * in SMP systems. :-) 227752557Sobrien */ 227852557Sobrien for (;;) { 227952557Sobrien /* 228018334Speter * dequeue acquires and adjusts the input_queue as it dequeues 228118334Speter * packets. It acquires the rw lock as needed. 228218334Speter */ 228318334Speter mtx_lock_spin(&ngq->q_mtx); 2284169699Skan item = ng_dequeue(ngq); /* fixes worklist too*/ 228518334Speter if (!item) { 228618334Speter mtx_unlock_spin(&ngq->q_mtx); 228718334Speter return (error); 228818334Speter } 228918334Speter mtx_unlock_spin(&ngq->q_mtx); 229018334Speter 229190285Sobrien /* 229218334Speter * Take over the reference frm the item. 229390285Sobrien * Hold it until the called function returns. 229418334Speter */ 229590285Sobrien 229618334Speter NGI_GET_NODE(item, node); /* zaps stored node */ 229718334Speter 229818334Speter /* 2299132727Skan * We have the appropriate lock, so run the item. 230018334Speter * When finished it will drop the lock accordingly 230118334Speter */ 230218334Speter ierror = ng_apply_item(node, item); 230318334Speter 2304169699Skan /* 230518334Speter * only return an error if it was our initial 2306169699Skan * item.. (compat hack) 230718334Speter */ 2308169699Skan if (oitem == item) { 2309169699Skan error = ierror; 2310169699Skan } 2311169699Skan 2312169699Skan /* 2313169699Skan * If the node goes away when we remove the reference, 2314169699Skan * whatever we just did caused it.. whatever we do, DO NOT 2315169699Skan * access the node again! 2316169699Skan */ 2317169699Skan if (NG_NODE_UNREF(node) == 0) { 2318169699Skan break; 2319169699Skan } 2320169699Skan } 2321169699Skan return (error); 2322169699Skan} 2323169699Skan 2324169699Skan/* 2325169699Skan * We have an item that was possibly queued somewhere. 2326169699Skan * It should contain all the information needed 2327169699Skan * to run it on the appropriate node/hook. 2328169699Skan */ 2329169699Skanstatic int 2330169699Skanng_apply_item(node_p node, item_p item) 2331169699Skan{ 2332169699Skan hook_p hook; 2333169699Skan int was_reader = ((item->el_flags & NGQF_RW)); 2334169699Skan int error = 0; 2335169699Skan ng_rcvdata_t *rcvdata; 2336169699Skan ng_rcvmsg_t *rcvmsg; 2337169699Skan 2338169699Skan NGI_GET_HOOK(item, hook); /* clears stored hook */ 2339169699Skan#ifdef NETGRAPH_DEBUG 2340169699Skan _ngi_check(item, __FILE__, __LINE__); 2341169699Skan#endif 2342169699Skan switch (item->el_flags & NGQF_TYPE) { 2343169699Skan case NGQF_DATA: 2344169699Skan /* 2345169699Skan * Check things are still ok as when we were queued. 2346169699Skan */ 2347169699Skan if ((hook == NULL) 2348169699Skan || NG_HOOK_NOT_VALID(hook) 2349169699Skan || NG_NODE_NOT_VALID(node) ) { 2350169699Skan error = EIO; 2351169699Skan NG_FREE_ITEM(item); 2352169699Skan break; 2353169699Skan } 2354169699Skan /* 2355169699Skan * If no receive method, just silently drop it. 2356169699Skan * Give preference to the hook over-ride method 2357169699Skan */ 2358169699Skan if ((!(rcvdata = hook->hk_rcvdata)) 2359169699Skan && (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) { 2360169699Skan error = 0; 2361169699Skan NG_FREE_ITEM(item); 2362169699Skan break; 2363169699Skan } 2364169699Skan error = (*rcvdata)(hook, item); 2365169699Skan break; 2366169699Skan case NGQF_MESG: 2367169699Skan if (hook) { 2368169699Skan if (NG_HOOK_NOT_VALID(hook)) { 2369169699Skan /* 2370169699Skan * The hook has been zapped then we can't 2371169699Skan * use it. Immediatly drop its reference. 2372169699Skan * The message may not need it. 2373169699Skan */ 2374169699Skan NG_HOOK_UNREF(hook); 2375169699Skan hook = NULL; 2376169699Skan } 2377169699Skan } 2378169699Skan /* 2379169699Skan * Similarly, if the node is a zombie there is 2380169699Skan * nothing we can do with it, drop everything. 2381169699Skan */ 2382169699Skan if (NG_NODE_NOT_VALID(node)) { 2383169699Skan TRAP_ERROR(); 2384169699Skan error = EINVAL; 2385169699Skan NG_FREE_ITEM(item); 2386169699Skan } else { 2387169699Skan /* 2388169699Skan * Call the appropriate message handler for the object. 2389169699Skan * It is up to the message handler to free the message. 2390169699Skan * If it's a generic message, handle it generically, 2391169699Skan * otherwise call the type's message handler 2392169699Skan * (if it exists) 2393169699Skan * XXX (race). Remember that a queued message may 2394169699Skan * reference a node or hook that has just been 2395169699Skan * invalidated. It will exist as the queue code 2396169699Skan * is holding a reference, but.. 2397169699Skan */ 2398169699Skan 239918334Speter struct ng_mesg *msg = NGI_MSG(item); 240090285Sobrien 2401169699Skan /* 240218334Speter * check if the generic handler owns it. 240318334Speter */ 240418334Speter if ((msg->header.typecookie == NGM_GENERIC_COOKIE) 240518334Speter && ((msg->header.flags & NGF_RESP) == 0)) { 240618334Speter error = ng_generic_msg(node, item, hook); 240718334Speter break; 240818334Speter } 2409169699Skan /* 2410169699Skan * Now see if there is a handler (hook or node specific) 2411169699Skan * in the target node. If none, silently discard. 2412169699Skan */ 2413169699Skan if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) 241418334Speter && (!(rcvmsg = node->nd_type->rcvmsg))) { 241518334Speter TRAP_ERROR(); 241618334Speter error = 0; 241790285Sobrien NG_FREE_ITEM(item); 2418169699Skan break; 241918334Speter } 242018334Speter error = (*rcvmsg)(node, item, hook); 242118334Speter } 2422169699Skan break; 2423169699Skan case NGQF_FN: 2424169699Skan /* 2425169699Skan * We have to implicitly trust the hook, 2426169699Skan * as some of these are used for system purposes 2427169699Skan * where the hook is invalid. In the case of 2428169699Skan * the shutdown message we allow it to hit 2429169699Skan * even if the node is invalid. 2430169699Skan */ 2431169699Skan if ((NG_NODE_NOT_VALID(node)) 2432169699Skan && (NGI_FN(item) != &ng_rmnode)) { 2433169699Skan TRAP_ERROR(); 243418334Speter error = EINVAL; 243518334Speter break; 243618334Speter } 243718334Speter (*NGI_FN(item))(node, hook, NGI_ARG1(item), NGI_ARG2(item)); 243818334Speter NG_FREE_ITEM(item); 243918334Speter break; 244018334Speter 244118334Speter } 2442132727Skan /* 244318334Speter * We held references on some of the resources 244418334Speter * that we took from the item. Now that we have 244518334Speter * finished doing everything, drop those references. 244618334Speter */ 244790285Sobrien if (hook) { 244818334Speter NG_HOOK_UNREF(hook); 244918334Speter } 245018334Speter 2451169699Skan if (was_reader) { 245218334Speter ng_leave_read(&node->nd_input_queue); 2453169699Skan } else { 245418334Speter ng_leave_write(&node->nd_input_queue); 245518334Speter } 245690285Sobrien return (error); 245718334Speter} 245818334Speter 245918334Speter/*********************************************************************** 246018334Speter * Implement the 'generic' control messages 246118334Speter ***********************************************************************/ 246218334Speterstatic int 246318334Speterng_generic_msg(node_p here, item_p item, hook_p lasthook) 246418334Speter{ 246518334Speter int error = 0; 246618334Speter struct ng_mesg *msg; 246718334Speter struct ng_mesg *resp = NULL; 246818334Speter 246918334Speter NGI_GET_MSG(item, msg); 247018334Speter if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 247118334Speter TRAP_ERROR(); 247218334Speter error = EINVAL; 247318334Speter goto out; 247418334Speter } 247518334Speter switch (msg->header.cmd) { 247618334Speter case NGM_SHUTDOWN: 247718334Speter ng_rmnode(here, NULL, NULL, 0); 247818334Speter break; 247918334Speter case NGM_MKPEER: 248018334Speter { 248118334Speter struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 248218334Speter 248318334Speter if (msg->header.arglen != sizeof(*mkp)) { 2484132727Skan TRAP_ERROR(); 248518334Speter error = EINVAL; 248618334Speter break; 248718334Speter } 248818334Speter mkp->type[sizeof(mkp->type) - 1] = '\0'; 248918334Speter mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 249018334Speter mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 249118334Speter error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 249218334Speter break; 249318334Speter } 249418334Speter case NGM_CONNECT: 249518334Speter { 249618334Speter struct ngm_connect *const con = 249718334Speter (struct ngm_connect *) msg->data; 249818334Speter node_p node2; 249918334Speter 250018334Speter if (msg->header.arglen != sizeof(*con)) { 250118334Speter TRAP_ERROR(); 250218334Speter error = EINVAL; 250318334Speter break; 250418334Speter } 250518334Speter con->path[sizeof(con->path) - 1] = '\0'; 250618334Speter con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 250718334Speter con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 250818334Speter /* Don't forget we get a reference.. */ 250952557Sobrien error = ng_path2noderef(here, con->path, &node2, NULL); 251018334Speter if (error) 251152557Sobrien break; 251252557Sobrien error = ng_con_nodes(here, con->ourhook, node2, con->peerhook); 251352557Sobrien NG_NODE_UNREF(node2); 251452557Sobrien break; 2515132727Skan } 2516132727Skan case NGM_NAME: 251718334Speter { 251890285Sobrien struct ngm_name *const nam = (struct ngm_name *) msg->data; 251990285Sobrien 252018334Speter if (msg->header.arglen != sizeof(*nam)) { 252118334Speter TRAP_ERROR(); 252218334Speter error = EINVAL; 252318334Speter break; 252418334Speter } 252518334Speter nam->name[sizeof(nam->name) - 1] = '\0'; 252618334Speter error = ng_name_node(here, nam->name); 252718334Speter break; 2528169699Skan } 2529169699Skan case NGM_RMHOOK: 253018334Speter { 2531169699Skan struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 2532169699Skan hook_p hook; 2533117404Skan 253418334Speter if (msg->header.arglen != sizeof(*rmh)) { 253518334Speter TRAP_ERROR(); 253618334Speter error = EINVAL; 253718334Speter break; 253818334Speter } 253918334Speter rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 254018334Speter if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 254118334Speter ng_destroy_hook(hook); 254218334Speter break; 254390285Sobrien } 254418334Speter case NGM_NODEINFO: 254518334Speter { 254618334Speter struct nodeinfo *ni; 254718334Speter 254818334Speter NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT); 254918334Speter if (resp == NULL) { 255018334Speter error = ENOMEM; 255190285Sobrien break; 255218334Speter } 255318334Speter 255418334Speter /* Fill in node info */ 255590285Sobrien ni = (struct nodeinfo *) resp->data; 255618334Speter if (NG_NODE_HAS_NAME(here)) 255718334Speter strncpy(ni->name, NG_NODE_NAME(here), NG_NODELEN); 255818334Speter strncpy(ni->type, here->nd_type->name, NG_TYPELEN); 255918334Speter ni->id = ng_node2ID(here); 256018334Speter ni->hooks = here->nd_numhooks; 256118334Speter break; 256218334Speter } 256318334Speter case NGM_LISTHOOKS: 256418334Speter { 256518334Speter const int nhooks = here->nd_numhooks; 256690285Sobrien struct hooklist *hl; 256718334Speter struct nodeinfo *ni; 256852557Sobrien hook_p hook; 256918334Speter 257018334Speter /* Get response struct */ 257118334Speter NG_MKRESPONSE(resp, msg, sizeof(*hl) 257218334Speter + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 257318334Speter if (resp == NULL) { 257418334Speter error = ENOMEM; 257518334Speter break; 257618334Speter } 257718334Speter hl = (struct hooklist *) resp->data; 257818334Speter ni = &hl->nodeinfo; 257918334Speter 258018334Speter /* Fill in node info */ 2581169699Skan if (NG_NODE_HAS_NAME(here)) 258218334Speter strncpy(ni->name, NG_NODE_NAME(here), NG_NODELEN); 258318334Speter strncpy(ni->type, here->nd_type->name, NG_TYPELEN); 258418334Speter ni->id = ng_node2ID(here); 258518334Speter 258618334Speter /* Cycle through the linked list of hooks */ 258718334Speter ni->hooks = 0; 258818334Speter LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) { 258918334Speter struct linkinfo *const link = &hl->link[ni->hooks]; 259090285Sobrien 259118334Speter if (ni->hooks >= nhooks) { 259218334Speter log(LOG_ERR, "%s: number of %s changed\n", 259318334Speter __func__, "hooks"); 259418334Speter break; 2595132727Skan } 2596132727Skan if (NG_HOOK_NOT_VALID(hook)) 2597132727Skan continue; 2598132727Skan strncpy(link->ourhook, NG_HOOK_NAME(hook), NG_HOOKLEN); 2599132727Skan strncpy(link->peerhook, 2600132727Skan NG_PEER_HOOK_NAME(hook), NG_HOOKLEN); 260118334Speter if (NG_PEER_NODE_NAME(hook)[0] != '\0') 260218334Speter strncpy(link->nodeinfo.name, 260352557Sobrien NG_PEER_NODE_NAME(hook), NG_NODELEN); 260452557Sobrien strncpy(link->nodeinfo.type, 260552557Sobrien NG_PEER_NODE(hook)->nd_type->name, NG_TYPELEN); 260652557Sobrien link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook)); 2607169699Skan link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks; 260852557Sobrien ni->hooks++; 2609169699Skan } 261052557Sobrien break; 261190285Sobrien } 261290285Sobrien 261352557Sobrien case NGM_LISTNAMES: 261452557Sobrien case NGM_LISTNODES: 261518334Speter { 261652557Sobrien const int unnamed = (msg->header.cmd == NGM_LISTNODES); 261718334Speter struct namelist *nl; 261890285Sobrien node_p node; 261990285Sobrien int num = 0; 262018334Speter 262152557Sobrien mtx_lock(&ng_nodelist_mtx); 262252557Sobrien /* Count number of nodes */ 262352557Sobrien LIST_FOREACH(node, &ng_nodelist, nd_nodes) { 262418334Speter if (NG_NODE_IS_VALID(node) 262552557Sobrien && (unnamed || NG_NODE_HAS_NAME(node))) { 262652557Sobrien num++; 262718334Speter } 262890285Sobrien } 262990285Sobrien mtx_unlock(&ng_nodelist_mtx); 263090285Sobrien 263118334Speter /* Get response struct */ 263218334Speter NG_MKRESPONSE(resp, msg, sizeof(*nl) 263318334Speter + (num * sizeof(struct nodeinfo)), M_NOWAIT); 263418334Speter if (resp == NULL) { 263518334Speter error = ENOMEM; 263618334Speter break; 263718334Speter } 263818334Speter nl = (struct namelist *) resp->data; 263918334Speter 264018334Speter /* Cycle through the linked list of nodes */ 264190285Sobrien nl->numnames = 0; 264290285Sobrien mtx_lock(&ng_nodelist_mtx); 264318334Speter LIST_FOREACH(node, &ng_nodelist, nd_nodes) { 264490285Sobrien struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; 264518334Speter 264618334Speter if (nl->numnames >= num) { 264718334Speter log(LOG_ERR, "%s: number of %s changed\n", 264818334Speter __func__, "nodes"); 264990285Sobrien break; 265018334Speter } 265118334Speter if (NG_NODE_NOT_VALID(node)) 2652132727Skan continue; 265318334Speter if (!unnamed && (! NG_NODE_HAS_NAME(node))) 2654132727Skan continue; 2655169699Skan if (NG_NODE_HAS_NAME(node)) 265618334Speter strncpy(np->name, NG_NODE_NAME(node), NG_NODELEN); 2657169699Skan strncpy(np->type, node->nd_type->name, NG_TYPELEN); 2658169699Skan np->id = ng_node2ID(node); 2659169699Skan np->hooks = node->nd_numhooks; 2660169699Skan nl->numnames++; 2661169699Skan } 2662169699Skan mtx_unlock(&ng_nodelist_mtx); 2663169699Skan break; 2664169699Skan } 2665169699Skan 2666169699Skan case NGM_LISTTYPES: 266752557Sobrien { 2668169699Skan struct typelist *tl; 2669169699Skan struct ng_type *type; 2670169699Skan int num = 0; 2671169699Skan 2672169699Skan mtx_lock(&ng_typelist_mtx); 2673169699Skan /* Count number of types */ 2674169699Skan LIST_FOREACH(type, &ng_typelist, types) { 2675169699Skan num++; 2676169699Skan } 2677169699Skan mtx_unlock(&ng_typelist_mtx); 2678169699Skan 2679169699Skan /* Get response struct */ 2680169699Skan NG_MKRESPONSE(resp, msg, sizeof(*tl) 2681169699Skan + (num * sizeof(struct typeinfo)), M_NOWAIT); 2682169699Skan if (resp == NULL) { 2683169699Skan error = ENOMEM; 2684169699Skan break; 2685169699Skan } 2686169699Skan tl = (struct typelist *) resp->data; 268790285Sobrien 2688169699Skan /* Cycle through the linked list of types */ 2689169699Skan tl->numtypes = 0; 2690169699Skan mtx_lock(&ng_typelist_mtx); 269118334Speter LIST_FOREACH(type, &ng_typelist, types) { 2692169699Skan struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 2693169699Skan 269418334Speter if (tl->numtypes >= num) { 2695169699Skan log(LOG_ERR, "%s: number of %s changed\n", 2696169699Skan __func__, "types"); 2697169699Skan break; 2698169699Skan } 2699169699Skan strncpy(tp->type_name, type->name, NG_TYPELEN); 2700169699Skan tp->numnodes = type->refs - 1; /* don't count list */ 2701169699Skan tl->numtypes++; 2702169699Skan } 2703169699Skan mtx_unlock(&ng_typelist_mtx); 2704169699Skan break; 2705169699Skan } 2706169699Skan 2707169699Skan case NGM_BINARY2ASCII: 2708169699Skan { 2709169699Skan int bufSize = 20 * 1024; /* XXX hard coded constant */ 2710169699Skan const struct ng_parse_type *argstype; 2711169699Skan const struct ng_cmdlist *c; 2712169699Skan struct ng_mesg *binary, *ascii; 2713169699Skan 2714169699Skan /* Data area must contain a valid netgraph message */ 2715169699Skan binary = (struct ng_mesg *)msg->data; 2716169699Skan if (msg->header.arglen < sizeof(struct ng_mesg) 2717169699Skan || (msg->header.arglen - sizeof(struct ng_mesg) 271818334Speter < binary->header.arglen)) { 271918334Speter TRAP_ERROR(); 272018334Speter error = EINVAL; 272118334Speter break; 272218334Speter } 272318334Speter 272418334Speter /* Get a response message with lots of room */ 272518334Speter NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 272690285Sobrien if (resp == NULL) { 272718334Speter error = ENOMEM; 272818334Speter break; 272918334Speter } 273018334Speter ascii = (struct ng_mesg *)resp->data; 273118334Speter 273290285Sobrien /* Copy binary message header to response message payload */ 273318334Speter bcopy(binary, ascii, sizeof(*binary)); 273418334Speter 2735117404Skan /* Find command by matching typecookie and command number */ 273618334Speter for (c = here->nd_type->cmdlist; 273718334Speter c != NULL && c->name != NULL; c++) { 273818334Speter if (binary->header.typecookie == c->cookie 273918334Speter && binary->header.cmd == c->cmd) 274018334Speter break; 274118334Speter } 274218334Speter if (c == NULL || c->name == NULL) { 274318334Speter for (c = ng_generic_cmds; c->name != NULL; c++) { 274418334Speter if (binary->header.typecookie == c->cookie 274518334Speter && binary->header.cmd == c->cmd) 274618334Speter break; 2747117404Skan } 2748132727Skan if (c->name == NULL) { 274918334Speter NG_FREE_MSG(resp); 2750117404Skan error = ENOSYS; 2751117404Skan break; 2752117404Skan } 2753117404Skan } 2754117404Skan 275550605Sobrien /* Convert command name to ASCII */ 275690285Sobrien snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 275750605Sobrien "%s", c->name); 2758169699Skan 275990285Sobrien /* Convert command arguments to ASCII */ 276090285Sobrien argstype = (binary->header.flags & NGF_RESP) ? 276190285Sobrien c->respType : c->mesgType; 276250605Sobrien if (argstype == NULL) { 276350605Sobrien *ascii->data = '\0'; 276452557Sobrien } else { 276552557Sobrien if ((error = ng_unparse(argstype, 276652557Sobrien (u_char *)binary->data, 276750605Sobrien ascii->data, bufSize)) != 0) { 276850605Sobrien NG_FREE_MSG(resp); 276990285Sobrien break; 277090285Sobrien } 2771117404Skan } 2772117404Skan 2773117404Skan /* Return the result as struct ng_mesg plus ASCII string */ 2774117404Skan bufSize = strlen(ascii->data) + 1; 277518334Speter ascii->header.arglen = bufSize; 277618334Speter resp->header.arglen = sizeof(*ascii) + bufSize; 277718334Speter break; 277852557Sobrien } 277990285Sobrien 278090285Sobrien case NGM_ASCII2BINARY: 278190285Sobrien { 278290285Sobrien int bufSize = 2000; /* XXX hard coded constant */ 278352557Sobrien const struct ng_cmdlist *c; 278490285Sobrien const struct ng_parse_type *argstype; 278590285Sobrien struct ng_mesg *ascii, *binary; 278618334Speter int off = 0; 278718334Speter 278850605Sobrien /* Data area must contain at least a struct ng_mesg + '\0' */ 278990285Sobrien ascii = (struct ng_mesg *)msg->data; 279050605Sobrien if ((msg->header.arglen < sizeof(*ascii) + 1) 279190285Sobrien || (ascii->header.arglen < 1) 279250605Sobrien || (msg->header.arglen 279350605Sobrien < sizeof(*ascii) + ascii->header.arglen)) { 279490285Sobrien TRAP_ERROR(); 279590285Sobrien error = EINVAL; 279690285Sobrien break; 279750605Sobrien } 279850605Sobrien ascii->data[ascii->header.arglen - 1] = '\0'; 279950605Sobrien 280050605Sobrien /* Get a response message with lots of room */ 280150605Sobrien NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 280290285Sobrien if (resp == NULL) { 280352557Sobrien error = ENOMEM; 2804169699Skan break; 2805169699Skan } 280650605Sobrien binary = (struct ng_mesg *)resp->data; 280750605Sobrien 280890285Sobrien /* Copy ASCII message header to response message payload */ 280990285Sobrien bcopy(ascii, binary, sizeof(*ascii)); 281090285Sobrien 281150605Sobrien /* Find command by matching ASCII command string */ 281290285Sobrien for (c = here->nd_type->cmdlist; 281350605Sobrien c != NULL && c->name != NULL; c++) { 2814169699Skan if (strcmp(ascii->header.cmdstr, c->name) == 0) 281550605Sobrien break; 281650605Sobrien } 281750605Sobrien if (c == NULL || c->name == NULL) { 281890285Sobrien for (c = ng_generic_cmds; c->name != NULL; c++) { 281990285Sobrien if (strcmp(ascii->header.cmdstr, c->name) == 0) 282090285Sobrien break; 282190285Sobrien } 282218334Speter if (c->name == NULL) { 282318334Speter NG_FREE_MSG(resp); 282418334Speter error = ENOSYS; 282518334Speter break; 282618334Speter } 282718334Speter } 282818334Speter 282990285Sobrien /* Convert command name to binary */ 283090285Sobrien binary->header.cmd = c->cmd; 283118334Speter binary->header.typecookie = c->cookie; 283290285Sobrien 283350605Sobrien /* Convert command arguments to binary */ 283450605Sobrien argstype = (binary->header.flags & NGF_RESP) ? 283590285Sobrien c->respType : c->mesgType; 283650605Sobrien if (argstype == NULL) { 283750605Sobrien bufSize = 0; 283890285Sobrien } else { 283950605Sobrien if ((error = ng_parse(argstype, ascii->data, 284090285Sobrien &off, (u_char *)binary->data, &bufSize)) != 0) { 284190285Sobrien NG_FREE_MSG(resp); 284250605Sobrien break; 284352557Sobrien } 284452557Sobrien } 284552557Sobrien 284652557Sobrien /* Return the result */ 284790285Sobrien binary->header.arglen = bufSize; 284890285Sobrien resp->header.arglen = sizeof(*binary) + bufSize; 284990285Sobrien break; 285090285Sobrien } 285118334Speter 285218334Speter case NGM_TEXT_CONFIG: 285318334Speter case NGM_TEXT_STATUS: 285490285Sobrien /* 285518334Speter * This one is tricky as it passes the command down to the 285690285Sobrien * actual node, even though it is a generic type command. 285790285Sobrien * This means we must assume that the item/msg is already freed 285890285Sobrien * when control passes back to us. 285990285Sobrien */ 286018334Speter if (here->nd_type->rcvmsg != NULL) { 286190285Sobrien NGI_MSG(item) = msg; /* put it back as we found it */ 286290285Sobrien return((*here->nd_type->rcvmsg)(here, item, lasthook)); 286390285Sobrien } 286418334Speter /* Fall through if rcvmsg not supported */ 286518334Speter default: 286618334Speter TRAP_ERROR(); 286718334Speter error = EINVAL; 286890285Sobrien } 286990285Sobrien /* 287018334Speter * Sometimes a generic message may be statically allocated 287118334Speter * to avoid problems with allocating when in tight memeory situations. 287218334Speter * Don't free it if it is so. 287318334Speter * I break them appart here, because erros may cause a free if the item 287418334Speter * in which case we'd be doing it twice. 287518334Speter * they are kept together above, to simplify freeing. 287618334Speter */ 287718334Speterout: 287818334Speter NG_RESPOND_MSG(error, here, item, resp); 287918334Speter if (msg) 288018334Speter NG_FREE_MSG(msg); 288150605Sobrien return (error); 288218334Speter} 288318334Speter 288418334Speter/* 288518334Speter * Copy a 'meta'. 288618334Speter * 288718334Speter * Returns new meta, or NULL if original meta is NULL or ENOMEM. 288818334Speter */ 288918334Spetermeta_p 289018334Speterng_copy_meta(meta_p meta) 289118334Speter{ 289218334Speter meta_p meta2; 289318334Speter 289418334Speter if (meta == NULL) 289518334Speter return (NULL); 289618334Speter MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH_META, M_NOWAIT); 289718334Speter if (meta2 == NULL) 289818334Speter return (NULL); 289918334Speter meta2->allocated_len = meta->used_len; 290018334Speter bcopy(meta, meta2, meta->used_len); 290118334Speter return (meta2); 290218334Speter} 290318334Speter 290418334Speter/************************************************************************ 290518334Speter Module routines 290618334Speter************************************************************************/ 290718334Speter 290890285Sobrien/* 290918334Speter * Handle the loading/unloading of a netgraph node type module 291018334Speter */ 291118334Speterint 291218334Speterng_mod_event(module_t mod, int event, void *data) 291318334Speter{ 291418334Speter struct ng_type *const type = data; 291518334Speter int s, error = 0; 291618334Speter 291790285Sobrien switch (event) { 2918132727Skan case MOD_LOAD: 2919132727Skan 292090285Sobrien /* Register new netgraph node type */ 292190285Sobrien s = splnet(); 292290285Sobrien if ((error = ng_newtype(type)) != 0) { 292318334Speter splx(s); 292418334Speter break; 292518334Speter } 292618334Speter 2927132727Skan /* Call type specific code */ 292890285Sobrien if (type->mod_event != NULL) 292918334Speter if ((error = (*type->mod_event)(mod, event, data))) { 293018334Speter mtx_lock(&ng_typelist_mtx); 293118334Speter type->refs--; /* undo it */ 293218334Speter LIST_REMOVE(type, types); 293318334Speter mtx_unlock(&ng_typelist_mtx); 293418334Speter } 293518334Speter splx(s); 293618334Speter break; 293718334Speter 293818334Speter case MOD_UNLOAD: 293950605Sobrien s = splnet(); 294090285Sobrien if (type->refs > 1) { /* make sure no nodes exist! */ 294150605Sobrien error = EBUSY; 2942169699Skan } else { 294350605Sobrien if (type->refs == 0) { 294450605Sobrien /* failed load, nothing to undo */ 294518334Speter splx(s); 294618334Speter break; 294718334Speter } 294818334Speter if (type->mod_event != NULL) { /* check with type */ 294918334Speter error = (*type->mod_event)(mod, event, data); 295018334Speter if (error != 0) { /* type refuses.. */ 295190285Sobrien splx(s); 295290285Sobrien break; 295390285Sobrien } 295490285Sobrien } 2955169699Skan mtx_lock(&ng_typelist_mtx); 295690285Sobrien LIST_REMOVE(type, types); 295790285Sobrien mtx_unlock(&ng_typelist_mtx); 2958117404Skan } 2959117404Skan splx(s); 2960117404Skan break; 2961117404Skan 2962117404Skan default: 2963117404Skan if (type->mod_event != NULL) 296490285Sobrien error = (*type->mod_event)(mod, event, data); 296590285Sobrien else 296690285Sobrien error = 0; /* XXX ? */ 296790285Sobrien break; 296890285Sobrien } 296918334Speter return (error); 297050605Sobrien} 297118334Speter 297218334Speter/* 297318334Speter * Handle loading and unloading for this code. 297418334Speter * The only thing we need to link into is the NETISR strucure. 297518334Speter */ 297618334Speterstatic int 297718334Speterngb_mod_event(module_t mod, int event, void *data) 297818334Speter{ 297918334Speter int s, error = 0; 298018334Speter 298118334Speter switch (event) { 298218334Speter case MOD_LOAD: 298318334Speter /* Register line discipline */ 298418334Speter mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_SPIN); 298518334Speter mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL, 0); 298618334Speter mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL, 0); 298790285Sobrien mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL, 0); 298850605Sobrien mtx_init(&ngq_mtx, "netgraph netisr mutex", NULL, 0); 298950605Sobrien s = splimp(); 299090285Sobrien error = register_netisr(NETISR_NETGRAPH, ngintr); 299150605Sobrien splx(s); 2992132727Skan break; 299350605Sobrien case MOD_UNLOAD: 299450605Sobrien /* You cant unload it because an interface may be using it. */ 299550605Sobrien error = EBUSY; 2996132727Skan break; 299750605Sobrien default: 2998169699Skan error = EOPNOTSUPP; 2999169699Skan break; 300018334Speter } 300150605Sobrien return (error); 300218334Speter} 300318334Speter 300418334Speterstatic moduledata_t netgraph_mod = { 300518334Speter "netgraph", 3006132727Skan ngb_mod_event, 300718334Speter (NULL) 300818334Speter}; 300918334SpeterDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 301018334SpeterSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family"); 301118334SpeterSYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,""); 301218334SpeterSYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, ""); 301318334Speter 3014169699Skan/************************************************************************ 301518334Speter Queue element get/free routines 301650605Sobrien************************************************************************/ 301750605Sobrien 301850605Sobrien 301990285Sobrienstatic int allocated; /* number of items malloc'd */ 302018334Speterstatic int maxalloc = 128; /* limit the damage of a leak */ 302118334Speterstatic const int ngqfreemax = 64;/* cache at most this many */ 302218334Speterstatic const int ngqfreelow = 4; /* try malloc if free < this */ 302318334Speterstatic volatile int ngqfreesize; /* number of cached entries */ 302418334Speter#ifdef NETGRAPH_DEBUG 302590285Sobrienstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist); 302618334Speter#endif 302718334Speter/* 302818334Speter * Get a queue entry 302918334Speter * This is usually called when a packet first enters netgraph. 303018334Speter * By definition, this is usually from an interrupt, or from a user. 303118334Speter * Users are not so important, but try be quick for the times that it's 303218334Speter * an interrupt. Use atomic operations to cope with collisions 303318334Speter * with interrupts and other processors. Assumes MALLOC is SMP safe. 303418334Speter * XXX If reserve is low, we should try to get 2 from malloc as this 303518334Speter * would indicate it often fails. 303618334Speter */ 303718334Speterstatic item_p 303818334Speterng_getqblk(void) 303918334Speter{ 304018334Speter item_p item = NULL; 304118334Speter 3042132727Skan /* 3043132727Skan * Try get a cached queue block, or else allocate a new one 304418334Speter * If we are less than our reserve, try malloc. If malloc 3045132727Skan * fails, then that's what the reserve is for... 3046132727Skan * Don't completely trust ngqfreesize, as it is subject 3047132727Skan * to races.. (it'll eventually catch up but may be out by one or two 3048132727Skan * for brief moments(under SMP or interrupts). 3049132727Skan * ngqfree is the final arbiter. We have our little reserve 3050132727Skan * because we use M_NOWAIT for malloc. This just helps us 3051132727Skan * avoid dropping packets while not increasing the time 305290285Sobrien * we take to service the interrupt (on average) (I hope). 305318334Speter */ 305418334Speter for (;;) { 305518334Speter if ((ngqfreesize < ngqfreelow) || (ngqfree == NULL)) { 3056132727Skan if (allocated < maxalloc) { /* don't leak forever */ 3057132727Skan MALLOC(item, item_p , 305818334Speter sizeof(*item), M_NETGRAPH_ITEM, 305918334Speter (M_NOWAIT | M_ZERO)); 306018334Speter if (item) { 306150605Sobrien#ifdef NETGRAPH_DEBUG 306218334Speter TAILQ_INSERT_TAIL(&ng_itemlist, 306318334Speter item, all); 306418334Speter#endif /* NETGRAPH_DEBUG */ 306550605Sobrien atomic_add_int(&allocated, 1); 306618334Speter break; 306718334Speter } 306818334Speter } 306918334Speter } 307018334Speter 3071132727Skan /* 307290285Sobrien * We didn't or couldn't malloc. 3073132727Skan * try get one from our cache. 3074132727Skan * item must be NULL to get here. 307518334Speter */ 307618334Speter if ((item = ngqfree) != NULL) { 307790285Sobrien /* 307890285Sobrien * Atomically try grab the first item 3079132727Skan * and put it's successor in its place. 3080132727Skan * If we fail, just try again.. someone else 3081132727Skan * beat us to this one or freed one. 308290285Sobrien * Don't worry about races with ngqfreesize. 3083132727Skan * Close enough is good enough.. 308418334Speter */ 308518334Speter if (atomic_cmpset_ptr(&ngqfree, item, item->el_next)) { 308618334Speter atomic_subtract_int(&ngqfreesize, 1); 308718334Speter item->el_flags &= ~NGQF_FREE; 308818334Speter break; 308918334Speter } 309018334Speter /* 3091132727Skan * something got there before we did.. try again 309218334Speter * (go around the loop again) 309318334Speter */ 309418334Speter item = NULL; 309518334Speter } else { 309618334Speter /* We really ran out */ 309718334Speter break; 309818334Speter } 3099132727Skan } 3100132727Skan return (item); 310190285Sobrien} 310290285Sobrien 3103132727Skan/* 310450605Sobrien * Release a queue entry 310550605Sobrien */ 310650605Sobrienvoid 310750605Sobrienng_free_item(item_p item) 3108132727Skan{ 3109169699Skan 3110132727Skan /* 3111132727Skan * The item may hold resources on it's own. We need to free 311250605Sobrien * these before we can free the item. What they are depends upon 311350605Sobrien * what kind of item it is. it is important that nodes zero 3114132727Skan * out pointers to resources that they remove from the item 311550605Sobrien * or we release them again here. 311618334Speter */ 311718334Speter if (item->el_flags & NGQF_FREE) { 311818334Speter panic(" Freeing free queue item"); 311918334Speter } 3120146906Skan switch (item->el_flags & NGQF_TYPE) { 312118334Speter case NGQF_DATA: 312218334Speter /* If we have an mbuf and metadata still attached.. */ 3123132727Skan NG_FREE_M(_NGI_M(item)); 312418334Speter NG_FREE_META(_NGI_META(item)); 3125132727Skan break; 3126132727Skan case NGQF_MESG: 312718334Speter _NGI_RETADDR(item) = NULL; 312818334Speter NG_FREE_MSG(_NGI_MSG(item)); 3129146906Skan break; 3130146906Skan case NGQF_FN: 3131146906Skan /* nothing to free really, */ 3132146906Skan _NGI_FN(item) = NULL; 3133146906Skan _NGI_ARG1(item) = NULL; 3134146906Skan _NGI_ARG2(item) = 0; 3135146906Skan case NGQF_UNDEF: 3136146906Skan } 3137146906Skan /* If we still have a node or hook referenced... */ 3138146906Skan _NGI_CLR_NODE(item); 3139146906Skan _NGI_CLR_HOOK(item); 3140146906Skan item->el_flags |= NGQF_FREE; 3141146906Skan 3142146906Skan /* 3143146906Skan * We have freed any resources held by the item. 314418334Speter * now we can free the item itself. 314590285Sobrien */ 3146132727Skan if (ngqfreesize < ngqfreemax) { /* don't worry about races */ 3147146906Skan for (;;) { 3148146906Skan item->el_next = ngqfree; 3149132727Skan if (atomic_cmpset_ptr(&ngqfree, item->el_next, item)) { 3150132727Skan break; 3151132727Skan } 315218334Speter } 315318334Speter atomic_add_int(&ngqfreesize, 1); 315418334Speter } else { 315518334Speter /* This is the only place that should use this Macro */ 315618334Speter#ifdef NETGRAPH_DEBUG 315718334Speter TAILQ_REMOVE(&ng_itemlist, item, all); 315818334Speter#endif /* NETGRAPH_DEBUG */ 3159132727Skan NG_FREE_ITEM_REAL(item); 316018334Speter atomic_subtract_int(&allocated, 1); 316118334Speter } 316218334Speter} 316318334Speter 316418334Speter#ifdef NETGRAPH_DEBUG 316590285Sobrienvoid 316618334Speterdumphook (hook_p hook, char *file, int line) 316718334Speter{ 316818334Speter printf("hook: name %s, %d refs, Last touched:\n", 316918334Speter _NG_HOOK_NAME(hook), hook->hk_refs); 317018334Speter printf(" Last active @ %s, line %d\n", 317118334Speter hook->lastfile, hook->lastline); 317218334Speter if (line) { 317318334Speter printf(" problem discovered at file %s, line %d\n", file, line); 317418334Speter } 3175169699Skan} 3176169699Skan 317718334Spetervoid 317896288Sobriendumpnode(node_p node, char *file, int line) 317918334Speter{ 318018334Speter printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n", 318118334Speter _NG_NODE_ID(node), node->nd_type->name, 318218334Speter node->nd_numhooks, node->nd_flags, 318318334Speter node->nd_refs, node->nd_name); 3184169699Skan printf(" Last active @ %s, line %d\n", 3185169699Skan node->lastfile, node->lastline); 318618334Speter if (line) { 318718334Speter printf(" problem discovered at file %s, line %d\n", file, line); 318818334Speter } 3189169699Skan} 319018334Speter 319118334Spetervoid 319218334Speterdumpitem(item_p item, char *file, int line) 319318334Speter{ 319418334Speter if (item->el_flags & NGQF_FREE) { 3195169699Skan printf(" Free item, freed at %s, line %d\n", 319618334Speter item->lastfile, item->lastline); 319718334Speter } else { 319818334Speter printf(" ACTIVE item, last used at %s, line %d", 319918334Speter item->lastfile, item->lastline); 320018334Speter switch(item->el_flags & NGQF_TYPE) { 320118334Speter case NGQF_DATA: 320218334Speter printf(" - [data]\n"); 3203169699Skan break; 320418334Speter case NGQF_MESG: 320518334Speter printf(" - retaddr[%d]:\n", _NGI_RETADDR(item)); 320618334Speter break; 320718334Speter case NGQF_FN: 320818334Speter printf(" - fn@%p (%p, %p, %p, %d (%x))\n", 320918334Speter item->body.fn.fn_fn, 321018334Speter NGI_NODE(item), 321118334Speter NGI_HOOK(item), 321218334Speter item->body.fn.fn_arg1, 321318334Speter item->body.fn.fn_arg2, 3214169699Skan item->body.fn.fn_arg2); 321518334Speter break; 321618334Speter case NGQF_UNDEF: 321718334Speter printf(" - UNDEFINED!\n"); 321818334Speter } 321918334Speter } 322018334Speter if (line) { 3221169699Skan printf(" problem discovered at file %s, line %d\n", file, line); 322218334Speter if (NGI_NODE(item)) { 322318334Speter printf("node %p ([%x])\n", 322418334Speter NGI_NODE(item), ng_node2ID(NGI_NODE(item))); 322518334Speter } 322618334Speter } 322718334Speter} 322818334Speter 322918334Speterstatic void 323018334Speterng_dumpitems(void) 3231169699Skan{ 323218334Speter item_p item; 323318334Speter int i = 1; 323418334Speter TAILQ_FOREACH(item, &ng_itemlist, all) { 323552557Sobrien printf("[%d] ", i++); 323652557Sobrien dumpitem(item, NULL, 0); 323752557Sobrien } 323852557Sobrien} 3239169699Skan 3240169699Skanstatic void 324118334Speterng_dumpnodes(void) 324218334Speter{ 324318334Speter node_p node; 324418334Speter int i = 1; 324518334Speter SLIST_FOREACH(node, &ng_allnodes, nd_all) { 324618334Speter printf("[%d] ", i++); 324718334Speter dumpnode(node, NULL, 0); 324818334Speter } 324918334Speter} 325018334Speter 3251169699Skanstatic void 3252169699Skanng_dumphooks(void) 325318334Speter{ 325418334Speter hook_p hook; 325518334Speter int i = 1; 325618334Speter SLIST_FOREACH(hook, &ng_allhooks, hk_all) { 325718334Speter printf("[%d] ", i++); 325818334Speter dumphook(hook, NULL, 0); 325918334Speter } 326018334Speter} 326118334Speter 326218334Speterstatic int 326318334Spetersysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS) 326418334Speter{ 326518334Speter int error; 3266117404Skan int val; 3267117404Skan int i; 3268117404Skan 3269117404Skan val = allocated; 327018334Speter i = 1; 327118334Speter error = sysctl_handle_int(oidp, &val, sizeof(int), req); 327218334Speter if (error != 0 || req->newptr == NULL) 327318334Speter return (error); 327418334Speter if (val == 42) { 327518334Speter ng_dumpitems(); 3276132727Skan ng_dumpnodes(); 327718334Speter ng_dumphooks(); 327818334Speter } 327918334Speter return (0); 328018334Speter} 328118334Speter 328218334SpeterSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW, 328318334Speter 0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items"); 328418334Speter#endif /* NETGRAPH_DEBUG */ 328518334Speter 328618334Speter 3287169699Skan/*********************************************************************** 328818334Speter* Worklist routines 328918334Speter**********************************************************************/ 329018334Speter/* NETISR thread enters here */ 329118334Speter/* 329218334Speter * Pick a node off the list of nodes with work, 329318334Speter * try get an item to process off it. 329418334Speter * If there are no more, remove the node from the list. 329518334Speter */ 329618334Speterstatic void 329718334Speterngintr(void) 329818334Speter{ 329918334Speter item_p item; 330018334Speter node_p node = NULL; 330118334Speter 330218334Speter for (;;) { 330318334Speter mtx_lock_spin(&ng_worklist_mtx); 330418334Speter node = TAILQ_FIRST(&ng_worklist); 330518334Speter if (!node) { 330618334Speter mtx_unlock_spin(&ng_worklist_mtx); 3307132727Skan break; 330818334Speter } 330918334Speter node->nd_flags &= ~NG_WORKQ; 331018334Speter TAILQ_REMOVE(&ng_worklist, node, nd_work); 331118334Speter mtx_unlock_spin(&ng_worklist_mtx); 3312169699Skan /* 331318334Speter * We have the node. We also take over the reference 331418334Speter * that the list had on it. 331518334Speter * Now process as much as you can, until it won't 331618334Speter * let you have another item off the queue. 331718334Speter * All this time, keep the reference 331818334Speter * that lets us be sure that the node still exists. 331918334Speter * Let the reference go at the last minute. 332018334Speter * ng_dequeue will put us back on the worklist 332118334Speter * if there is more too do. This may be of use if there 332218334Speter * are Multiple Processors and multiple Net threads in the 332390285Sobrien * future. 332490285Sobrien */ 332518334Speter for (;;) { 332618334Speter mtx_lock_spin(&node->nd_input_queue.q_mtx); 3327169699Skan item = ng_dequeue(&node->nd_input_queue); 332818334Speter if (item == NULL) { 332918334Speter mtx_unlock_spin(&node->nd_input_queue.q_mtx); 333018334Speter break; /* go look for another node */ 333190285Sobrien } else { 333218334Speter mtx_unlock_spin(&node->nd_input_queue.q_mtx); 333318334Speter NGI_GET_NODE(item, node); /* zaps stored node */ 333418334Speter ng_apply_item(node, item); 333518334Speter NG_NODE_UNREF(node); 333618334Speter } 333718334Speter } 333890285Sobrien NG_NODE_UNREF(node); 3339132727Skan } 334090285Sobrien} 3341132727Skan 3342132727Skanstatic void 3343117404Skanng_worklist_remove(node_p node) 3344117404Skan{ 3345117404Skan mtx_lock_spin(&ng_worklist_mtx); 3346132727Skan if (node->nd_flags & NG_WORKQ) { 3347117404Skan node->nd_flags &= ~NG_WORKQ; 3348117404Skan TAILQ_REMOVE(&ng_worklist, node, nd_work); 3349117404Skan mtx_unlock_spin(&ng_worklist_mtx); 3350169699Skan NG_NODE_UNREF(node); 3351169699Skan } else { 3352117404Skan mtx_unlock_spin(&ng_worklist_mtx); 3353117404Skan } 3354117404Skan} 3355117404Skan 3356169699Skan/* 3357132727Skan * XXX 3358132727Skan * It's posible that a debugging NG_NODE_REF may need 3359132727Skan * to be outside the mutex zone 3360132727Skan */ 3361132727Skanstatic void 3362117404Skanng_setisr(node_p node) 3363117404Skan{ 3364117404Skan mtx_lock_spin(&ng_worklist_mtx); 3365117404Skan if ((node->nd_flags & NG_WORKQ) == 0) { 3366117404Skan /* 3367169699Skan * If we are not already on the work queue, 3368169699Skan * then put us on. 3369117404Skan */ 3370117404Skan node->nd_flags |= NG_WORKQ; 3371117404Skan TAILQ_INSERT_TAIL(&ng_worklist, node, nd_work); 3372117404Skan NG_NODE_REF(node); /* XXX fafe in mutex? */ 3373117404Skan } 3374132727Skan mtx_unlock_spin(&ng_worklist_mtx); 3375117404Skan schednetisr(NETISR_NETGRAPH); 3376132727Skan} 3377117404Skan 3378117404Skan 3379117404Skan/*********************************************************************** 3380117404Skan* Externally useable functions to set up a queue item ready for sending 3381169699Skan***********************************************************************/ 3382169699Skan 3383117404Skan#ifdef NETGRAPH_DEBUG 3384117404Skan#define ITEM_DEBUG_CHECKS \ 3385117404Skan do { \ 3386117404Skan if (NGI_NODE(item) ) { \ 3387132727Skan printf("item already has node"); \ 338890285Sobrien Debugger("has node"); \ 338918334Speter NGI_CLR_NODE(item); \ 339090285Sobrien } \ 339190285Sobrien if (NGI_HOOK(item) ) { \ 339290285Sobrien printf("item already has hook"); \ 339318334Speter Debugger("has hook"); \ 3394132727Skan NGI_CLR_HOOK(item); \ 3395132727Skan } \ 3396132727Skan } while (0) 339718334Speter#else 339818334Speter#define ITEM_DEBUG_CHECKS 339918334Speter#endif 340018334Speter 3401169699Skan/* 340218334Speter * Put elements into the item. 340390285Sobrien * Hook and node references will be removed when the item is dequeued. 340418334Speter * (or equivalent) 340518334Speter * (XXX) Unsafe because no reference held by peer on remote node. 340618334Speter * remote node might go away in this timescale. 3407132727Skan * We know the hooks can't go away because that would require getting 340818334Speter * a writer item on both nodes and we must have at least a reader 340918334Speter * here to eb able to do this. 341018334Speter * Note that the hook loaded is the REMOTE hook. 341118334Speter * 341218334Speter * This is possibly in the critical path for new data. 341318334Speter */ 341418334Speteritem_p 341518334Speterng_package_data(struct mbuf *m, meta_p meta) 341618334Speter{ 341718334Speter item_p item; 341818334Speter 341918334Speter if ((item = ng_getqblk()) == NULL) { 342090285Sobrien NG_FREE_M(m); 342190285Sobrien NG_FREE_META(meta); 342218334Speter return (NULL); 342318334Speter } 342418334Speter ITEM_DEBUG_CHECKS; 342518334Speter item->el_flags = NGQF_DATA; 342618334Speter item->el_next = NULL; 342718334Speter NGI_M(item) = m; 342818334Speter NGI_META(item) = meta; 342918334Speter return (item); 343018334Speter} 3431169699Skan 343218334Speter/* 343318334Speter * Allocate a queue item and put items into it.. 343418334Speter * Evaluate the address as this will be needed to queue it and 343518334Speter * to work out what some of the fields should be. 343618334Speter * Hook and node references will be removed when the item is dequeued. 343718334Speter * (or equivalent) 343818334Speter */ 343918334Speteritem_p 344018334Speterng_package_msg(struct ng_mesg *msg) 344190285Sobrien{ 344218334Speter item_p item; 344318334Speter 3444169699Skan if ((item = ng_getqblk()) == NULL) { 344550605Sobrien NG_FREE_MSG(msg); 344690285Sobrien return (NULL); 344750605Sobrien } 344850605Sobrien ITEM_DEBUG_CHECKS; 344918334Speter item->el_flags = NGQF_MESG; 345018334Speter item->el_next = NULL; 345118334Speter /* 345218334Speter * Set the current lasthook into the queue item 345318334Speter */ 345418334Speter NGI_MSG(item) = msg; 345518334Speter NGI_RETADDR(item) = NULL; 345650605Sobrien return (item); 345750605Sobrien} 345850605Sobrien 3459169699Skan 346090285Sobrien 346190285Sobrien#define SET_RETADDR(item, here, retaddr) \ 346250605Sobrien do { /* Data or fn items don't have retaddrs */ \ 346350605Sobrien if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) { \ 346450605Sobrien if (retaddr) { \ 346550605Sobrien NGI_RETADDR(item) = retaddr; \ 346650605Sobrien } else { \ 3467169699Skan /* \ 3468169699Skan * The old return address should be ok. \ 3469169699Skan * If there isn't one, use the address \ 3470169699Skan * here. \ 3471169699Skan */ \ 3472169699Skan if (NGI_RETADDR(item) == 0) { \ 3473169699Skan NGI_RETADDR(item) \ 3474169699Skan = ng_node2ID(here); \ 3475169699Skan } \ 3476169699Skan } \ 3477169699Skan } \ 3478169699Skan } while (0) 3479169699Skan 3480169699Skanint 3481169699Skanng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr) 3482169699Skan{ 3483169699Skan hook_p peer; 3484169699Skan node_p peernode; 3485169699Skan ITEM_DEBUG_CHECKS; 3486132727Skan /* 3487169699Skan * Quick sanity check.. 3488132727Skan * Since a hook holds a reference on it's node, once we know 348918334Speter * that the peer is still connected (even if invalid,) we know 349018334Speter * that the peer node is present, though maybe invalid. 349118334Speter */ 349290285Sobrien if ((hook == NULL) 349318334Speter || NG_HOOK_NOT_VALID(hook) 349418334Speter || (NG_HOOK_PEER(hook) == NULL) 349518334Speter || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)) 349618334Speter || NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) { 3497169699Skan NG_FREE_ITEM(item); 349818334Speter TRAP_ERROR(); 349918334Speter return (ENETDOWN); 350018334Speter } 350150605Sobrien 350250605Sobrien /* 350350605Sobrien * Transfer our interest to the other (peer) end. 350450605Sobrien */ 350550605Sobrien peer = NG_HOOK_PEER(hook); 350650605Sobrien NG_HOOK_REF(peer); 350718334Speter NGI_SET_HOOK(item, peer); 350818334Speter peernode = NG_PEER_NODE(hook); 350918334Speter NG_NODE_REF(peernode); 351090285Sobrien NGI_SET_NODE(item, peernode); 351118334Speter SET_RETADDR(item, here, retaddr); 351218334Speter return (0); 351318334Speter} 351418334Speter 351518334Speterint 351618334Speterng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr) 351718334Speter{ 351818334Speter node_p dest = NULL; 351918334Speter hook_p hook = NULL; 352018334Speter int error; 352118334Speter 352218334Speter ITEM_DEBUG_CHECKS; 352318334Speter /* 352418334Speter * Note that ng_path2noderef increments the reference count 352518334Speter * on the node for us if it finds one. So we don't have to. 352618334Speter */ 352718334Speter error = ng_path2noderef(here, address, &dest, &hook); 352818334Speter if (error) { 352918334Speter NG_FREE_ITEM(item); 353018334Speter return (error); 353190285Sobrien } 353290285Sobrien NGI_SET_NODE(item, dest); 353318334Speter if ( hook) { 3534169699Skan NG_HOOK_REF(hook); /* don't let it go while on the queue */ 3535169699Skan NGI_SET_HOOK(item, hook); 353618334Speter } 353718334Speter SET_RETADDR(item, here, retaddr); 353818334Speter return (0); 353918334Speter} 354018334Speter 354118334Speterint 354290285Sobrienng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr) 354318334Speter{ 354418334Speter node_p dest; 354518334Speter 354618334Speter ITEM_DEBUG_CHECKS; 354718334Speter /* 354850605Sobrien * Find the target node. 354918334Speter */ 355018334Speter dest = ng_ID2noderef(ID); /* GETS REFERENCE! */ 355118334Speter if (dest == NULL) { 355218334Speter NG_FREE_ITEM(item); 355318334Speter TRAP_ERROR(); 355418334Speter return(EINVAL); 355518334Speter } 355618334Speter /* Fill out the contents */ 355718334Speter item->el_flags = NGQF_MESG; 355818334Speter item->el_next = NULL; 355990285Sobrien NGI_SET_NODE(item, dest); 356018334Speter NGI_CLR_HOOK(item); 356190285Sobrien SET_RETADDR(item, here, retaddr); 356218334Speter return (0); 356390285Sobrien} 356418334Speter 3565169699Skan/* 356690285Sobrien * special case to send a message to self (e.g. destroy node) 356718334Speter * Possibly indicate an arrival hook too. 356818334Speter * Useful for removing that hook :-) 356918334Speter */ 3570169699Skanitem_p 3571169699Skanng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg) 3572169699Skan{ 357318334Speter item_p item; 357418334Speter 357518334Speter /* 357618334Speter * Find the target node. 3577169699Skan * If there is a HOOK argument, then use that in preference 357818334Speter * to the address. 357918334Speter */ 358018334Speter if ((item = ng_getqblk()) == NULL) { 358190285Sobrien NG_FREE_MSG(msg); 358218334Speter return (NULL); 358318334Speter } 358418334Speter 358590285Sobrien /* Fill out the contents */ 358690285Sobrien item->el_flags = NGQF_MESG; 358718334Speter item->el_next = NULL; 358890285Sobrien NG_NODE_REF(here); 358990285Sobrien NGI_SET_NODE(item, here); 359018334Speter if (hook) { 3591169699Skan NG_HOOK_REF(hook); 359218334Speter NGI_SET_HOOK(item, hook); 3593169699Skan } 3594169699Skan NGI_MSG(item) = msg; 359590285Sobrien NGI_RETADDR(item) = ng_node2ID(here); 359618334Speter return (item); 359718334Speter} 359818334Speter 359990285Sobrienint 360018334Speterng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2) 360118334Speter{ 360218334Speter item_p item; 360318334Speter 360418334Speter if ((item = ng_getqblk()) == NULL) { 360518334Speter return (ENOMEM); 360618334Speter } 360718334Speter item->el_flags = NGQF_FN | NGQF_WRITER; 360818334Speter NG_NODE_REF(node); /* and one for the item */ 360918334Speter NGI_SET_NODE(item, node); 361090285Sobrien if (hook) { 361118334Speter NG_HOOK_REF(hook); 361218334Speter NGI_SET_HOOK(item, hook); 361390285Sobrien } 361418334Speter NGI_FN(item) = fn; 361518334Speter NGI_ARG1(item) = arg1; 361690285Sobrien NGI_ARG2(item) = arg2; 361718334Speter return(ng_snd_item(item, 0)); 361818334Speter} 361918334Speter 362018334Speter/* 362118334Speter * Official timeout routines for Netgraph nodes. 362218334Speter */ 362318334Speterstatic void 362418334Speterng_timeout_trapoline(void *arg) 362518334Speter{ 362618334Speter item_p item = arg; 362718334Speter 362818334Speter ng_snd_item(item, 0); 362990285Sobrien} 363090285Sobrien 363118334Speter 363218334Speterstruct callout_handle 363318334Speterng_timeout(node_p node, hook_p hook, int ticks, 363418334Speter ng_item_fn *fn, void * arg1, int arg2) 363590285Sobrien{ 363690285Sobrien item_p item; 363718334Speter 363818334Speter if ((item = ng_getqblk()) == NULL) { 363918334Speter struct callout_handle handle; 364018334Speter handle.callout = NULL; 364118334Speter return (handle); 364218334Speter } 364318334Speter item->el_flags = NGQF_FN | NGQF_WRITER; 364418334Speter NG_NODE_REF(node); /* and one for the item */ 364518334Speter NGI_SET_NODE(item, node); 364618334Speter if (hook) { 364718334Speter NG_HOOK_REF(hook); 364818334Speter NGI_SET_HOOK(item, hook); 364918334Speter } 365018334Speter NGI_FN(item) = fn; 365150605Sobrien NGI_ARG1(item) = arg1; 365250605Sobrien NGI_ARG2(item) = arg2; 365350605Sobrien return (timeout(&ng_timeout_trapoline, item, ticks)); 365418334Speter} 365518334Speter 365618334Speter/* A special modified version of untimeout() */ 365718334Speterint 365818334Speterng_untimeout(struct callout_handle handle, node_p node) 365918334Speter{ 366018334Speter item_p item; 366118334Speter 366218334Speter if (handle.callout == NULL) 366318334Speter return (0); 366490285Sobrien mtx_lock_spin(&callout_lock); 366518334Speter item = handle.callout->c_arg; /* should be an official way to do this */ 366618334Speter if ((handle.callout->c_func == &ng_timeout_trapoline) && 366718334Speter (NGI_NODE(item) == node) && 366818334Speter (callout_stop(handle.callout))) { 366918334Speter /* 367018334Speter * We successfully removed it from the queue before it ran 367118334Speter * So now we need to unreference everything that was 367218334Speter * given extra references. (NG_FREE_ITEM does this). 367318334Speter */ 367418334Speter mtx_unlock_spin(&callout_lock); 367518334Speter NG_FREE_ITEM(item); 367618334Speter return (1); 367718334Speter } 367818334Speter mtx_unlock_spin(&callout_lock); 367918334Speter return (0); 368018334Speter} 368118334Speter 368218334Speter/* 368318334Speter * Set the address, if none given, give the node here. 368418334Speter */ 368518334Spetervoid 368618334Speterng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr) 368718334Speter{ 368818334Speter if (retaddr) { 368918334Speter NGI_RETADDR(item) = retaddr; 369018334Speter } else { 369190285Sobrien /* 369290285Sobrien * The old return address should be ok. 369318334Speter * If there isn't one, use the address here. 369490285Sobrien */ 369590285Sobrien NGI_RETADDR(item) = ng_node2ID(here); 369690285Sobrien } 369790285Sobrien} 369890285Sobrien 369990285Sobrien#define TESTING 370090285Sobrien#ifdef TESTING 370190285Sobrien/* just test all the macros */ 370218334Spetervoid 370318334Speterng_macro_test(item_p item); 370418334Spetervoid 370518334Speterng_macro_test(item_p item) 370618334Speter{ 370718334Speter node_p node = NULL; 370818334Speter hook_p hook = NULL; 370918334Speter struct mbuf *m; 371018334Speter meta_p meta; 3711169699Skan struct ng_mesg *msg; 3712169699Skan ng_ID_t retaddr; 3713169699Skan int error; 3714169699Skan 371590285Sobrien NGI_GET_M(item, m); 371690285Sobrien NGI_GET_META(item, meta); 371718334Speter NGI_GET_MSG(item, msg); 371818334Speter retaddr = NGI_RETADDR(item); 371918334Speter NG_SEND_DATA(error, hook, m, meta); 372018334Speter NG_SEND_DATA_ONLY(error, hook, m); 372190285Sobrien NG_FWD_NEW_DATA(error, item, hook, m); 372290285Sobrien NG_FWD_ITEM_HOOK(error, item, hook); 372390285Sobrien NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr); 372490285Sobrien NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr); 372590285Sobrien NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr); 372690285Sobrien NG_FWD_MSG_HOOK(error, node, item, hook, retaddr); 372790285Sobrien} 372890285Sobrien#endif /* TESTING */ 372990285Sobrien 373018334Speter