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