ng_base.c revision 171885
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 171885 2007-08-18 11:59:17Z mav $ 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); 195170180Sglebiusstatic int 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 231168049Swkoszek#define NG_QUEUE_LOCK_INIT(n) \ 232168137Swkoszek mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF) 233168049Swkoszek#define NG_QUEUE_LOCK(n) \ 234168137Swkoszek mtx_lock(&(n)->q_mtx) 235168049Swkoszek#define NG_QUEUE_UNLOCK(n) \ 236168137Swkoszek mtx_unlock(&(n)->q_mtx) 237168049Swkoszek#define NG_WORKLIST_LOCK_INIT() \ 238168137Swkoszek mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF) 239168049Swkoszek#define NG_WORKLIST_LOCK() \ 240168137Swkoszek mtx_lock(&ng_worklist_mtx) 241168049Swkoszek#define NG_WORKLIST_UNLOCK() \ 242168137Swkoszek mtx_unlock(&ng_worklist_mtx) 243168049Swkoszek 24470784Sjulian#ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ 24570784Sjulian/* 24670784Sjulian * In debug mode: 24770784Sjulian * In an attempt to help track reference count screwups 24870784Sjulian * we do not free objects back to the malloc system, but keep them 24970784Sjulian * in a local cache where we can examine them and keep information safely 25070784Sjulian * after they have been freed. 25170784Sjulian * We use this scheme for nodes and hooks, and to some extent for items. 25270784Sjulian */ 25370784Sjulianstatic __inline hook_p 25470784Sjulianng_alloc_hook(void) 25570784Sjulian{ 25670784Sjulian hook_p hook; 25770784Sjulian SLIST_ENTRY(ng_hook) temp; 25872200Sbmilekic mtx_lock(&ng_nodelist_mtx); 25970784Sjulian hook = LIST_FIRST(&ng_freehooks); 26070784Sjulian if (hook) { 26170784Sjulian LIST_REMOVE(hook, hk_hooks); 26270784Sjulian bcopy(&hook->hk_all, &temp, sizeof(temp)); 26370784Sjulian bzero(hook, sizeof(struct ng_hook)); 26470784Sjulian bcopy(&temp, &hook->hk_all, sizeof(temp)); 26572200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 26670784Sjulian hook->hk_magic = HK_MAGIC; 26770784Sjulian } else { 26872200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 26970784Sjulian _NG_ALLOC_HOOK(hook); 27070784Sjulian if (hook) { 27170784Sjulian hook->hk_magic = HK_MAGIC; 27272200Sbmilekic mtx_lock(&ng_nodelist_mtx); 27370784Sjulian SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all); 27472200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 27570784Sjulian } 27670784Sjulian } 27770784Sjulian return (hook); 27870784Sjulian} 27970784Sjulian 28070784Sjulianstatic __inline node_p 28170784Sjulianng_alloc_node(void) 28270784Sjulian{ 28370784Sjulian node_p node; 28470784Sjulian SLIST_ENTRY(ng_node) temp; 28572200Sbmilekic mtx_lock(&ng_nodelist_mtx); 28670784Sjulian node = LIST_FIRST(&ng_freenodes); 28770784Sjulian if (node) { 28870784Sjulian LIST_REMOVE(node, nd_nodes); 28970784Sjulian bcopy(&node->nd_all, &temp, sizeof(temp)); 29070784Sjulian bzero(node, sizeof(struct ng_node)); 29170784Sjulian bcopy(&temp, &node->nd_all, sizeof(temp)); 29272200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 29370784Sjulian node->nd_magic = ND_MAGIC; 29470784Sjulian } else { 29572200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 29670784Sjulian _NG_ALLOC_NODE(node); 29770784Sjulian if (node) { 29870784Sjulian node->nd_magic = ND_MAGIC; 29972200Sbmilekic mtx_lock(&ng_nodelist_mtx); 30070784Sjulian SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all); 30172200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 30270784Sjulian } 30370784Sjulian } 30470784Sjulian return (node); 30570784Sjulian} 30670784Sjulian 30770784Sjulian#define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0) 30870784Sjulian#define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0) 30970784Sjulian 31070784Sjulian 31170784Sjulian#define NG_FREE_HOOK(hook) \ 31270784Sjulian do { \ 31372200Sbmilekic mtx_lock(&ng_nodelist_mtx); \ 31470784Sjulian LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks); \ 31570784Sjulian hook->hk_magic = 0; \ 31672200Sbmilekic mtx_unlock(&ng_nodelist_mtx); \ 31770784Sjulian } while (0) 31870784Sjulian 31970784Sjulian#define NG_FREE_NODE(node) \ 32070784Sjulian do { \ 32172200Sbmilekic mtx_lock(&ng_nodelist_mtx); \ 32270784Sjulian LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes); \ 32370784Sjulian node->nd_magic = 0; \ 32472200Sbmilekic mtx_unlock(&ng_nodelist_mtx); \ 32570784Sjulian } while (0) 32670784Sjulian 32770784Sjulian#else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ 32870784Sjulian 32970784Sjulian#define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook) 33070784Sjulian#define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node) 33170784Sjulian 33270700Sjulian#define NG_FREE_HOOK(hook) do { FREE((hook), M_NETGRAPH_HOOK); } while (0) 33370700Sjulian#define NG_FREE_NODE(node) do { FREE((node), M_NETGRAPH_NODE); } while (0) 33470784Sjulian 33570784Sjulian#endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ 33670784Sjulian 337131933Smarcel/* Set this to kdb_enter("X") to catch all errors as they occur */ 33852419Sjulian#ifndef TRAP_ERROR 33971047Sjulian#define TRAP_ERROR() 34052419Sjulian#endif 34152419Sjulian 34252722Sjulianstatic ng_ID_t nextID = 1; 34352722Sjulian 34453403Sarchie#ifdef INVARIANTS 34553403Sarchie#define CHECK_DATA_MBUF(m) do { \ 34653403Sarchie struct mbuf *n; \ 34753403Sarchie int total; \ 34853403Sarchie \ 349113255Sdes M_ASSERTPKTHDR(m); \ 350149818Sglebius for (total = 0, n = (m); n != NULL; n = n->m_next) { \ 35153403Sarchie total += n->m_len; \ 352149818Sglebius if (n->m_nextpkt != NULL) \ 353149818Sglebius panic("%s: m_nextpkt", __func__); \ 354149818Sglebius } \ 355149827Sglebius \ 35653403Sarchie if ((m)->m_pkthdr.len != total) { \ 35753403Sarchie panic("%s: %d != %d", \ 35887599Sobrien __func__, (m)->m_pkthdr.len, total); \ 35953403Sarchie } \ 36053403Sarchie } while (0) 36153403Sarchie#else 36253403Sarchie#define CHECK_DATA_MBUF(m) 36353403Sarchie#endif 36452722Sjulian 36553403Sarchie 36652419Sjulian/************************************************************************ 36753913Sarchie Parse type definitions for generic messages 36853913Sarchie************************************************************************/ 36953913Sarchie 37053913Sarchie/* Handy structure parse type defining macro */ 37153913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ 37297685Sarchiestatic const struct ng_parse_struct_field \ 37397685Sarchie ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args; \ 37453913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = { \ 37553913Sarchie &ng_parse_struct_type, \ 37697685Sarchie &ng_ ## lo ## _type_fields \ 37753913Sarchie} 37853913Sarchie 37953913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); 38053913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); 38153913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); 38253913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); 38353913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); 38453913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); 38553913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); 38653913Sarchie 38753913Sarchie/* Get length of an array when the length is stored as a 32 bit 38872645Sasmodai value immediately preceding the array -- as with struct namelist 38953913Sarchie and struct typelist. */ 39053913Sarchiestatic int 39153913Sarchieng_generic_list_getLength(const struct ng_parse_type *type, 39253913Sarchie const u_char *start, const u_char *buf) 39353913Sarchie{ 39453913Sarchie return *((const u_int32_t *)(buf - 4)); 39553913Sarchie} 39653913Sarchie 39753913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */ 39853913Sarchiestatic int 39953913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type, 40053913Sarchie const u_char *start, const u_char *buf) 40153913Sarchie{ 40253913Sarchie const struct hooklist *hl = (const struct hooklist *)start; 40353913Sarchie 40453913Sarchie return hl->nodeinfo.hooks; 40553913Sarchie} 40653913Sarchie 40753913Sarchie/* Array type for a variable length array of struct namelist */ 40853913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = { 40953913Sarchie &ng_generic_nodeinfo_type, 41053913Sarchie &ng_generic_list_getLength 41153913Sarchie}; 41253913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = { 41353913Sarchie &ng_parse_array_type, 41453913Sarchie &ng_nodeinfoarray_type_info 41553913Sarchie}; 41653913Sarchie 41753913Sarchie/* Array type for a variable length array of struct typelist */ 41853913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = { 41953913Sarchie &ng_generic_typeinfo_type, 42053913Sarchie &ng_generic_list_getLength 42153913Sarchie}; 42253913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = { 42353913Sarchie &ng_parse_array_type, 42453913Sarchie &ng_typeinfoarray_type_info 42553913Sarchie}; 42653913Sarchie 42753913Sarchie/* Array type for array of struct linkinfo in struct hooklist */ 42853913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { 42953913Sarchie &ng_generic_linkinfo_type, 43053913Sarchie &ng_generic_linkinfo_getLength 43153913Sarchie}; 43253913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = { 43353913Sarchie &ng_parse_array_type, 43453913Sarchie &ng_generic_linkinfo_array_type_info 43553913Sarchie}; 43653913Sarchie 43753913SarchieDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); 43853913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, 43953913Sarchie (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); 44053913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, 44153913Sarchie (&ng_generic_nodeinfoarray_type)); 44253913Sarchie 44353913Sarchie/* List of commands and how to convert arguments to/from ASCII */ 44453913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = { 44553913Sarchie { 44653913Sarchie NGM_GENERIC_COOKIE, 44753913Sarchie NGM_SHUTDOWN, 44853913Sarchie "shutdown", 44953913Sarchie NULL, 45053913Sarchie NULL 45153913Sarchie }, 45253913Sarchie { 45353913Sarchie NGM_GENERIC_COOKIE, 45453913Sarchie NGM_MKPEER, 45553913Sarchie "mkpeer", 45653913Sarchie &ng_generic_mkpeer_type, 45753913Sarchie NULL 45853913Sarchie }, 45953913Sarchie { 46053913Sarchie NGM_GENERIC_COOKIE, 46153913Sarchie NGM_CONNECT, 46253913Sarchie "connect", 46353913Sarchie &ng_generic_connect_type, 46453913Sarchie NULL 46553913Sarchie }, 46653913Sarchie { 46753913Sarchie NGM_GENERIC_COOKIE, 46853913Sarchie NGM_NAME, 46953913Sarchie "name", 47053913Sarchie &ng_generic_name_type, 47153913Sarchie NULL 47253913Sarchie }, 47353913Sarchie { 47453913Sarchie NGM_GENERIC_COOKIE, 47553913Sarchie NGM_RMHOOK, 47653913Sarchie "rmhook", 47753913Sarchie &ng_generic_rmhook_type, 47853913Sarchie NULL 47953913Sarchie }, 48053913Sarchie { 48153913Sarchie NGM_GENERIC_COOKIE, 48253913Sarchie NGM_NODEINFO, 48353913Sarchie "nodeinfo", 48453913Sarchie NULL, 48553913Sarchie &ng_generic_nodeinfo_type 48653913Sarchie }, 48753913Sarchie { 48853913Sarchie NGM_GENERIC_COOKIE, 48953913Sarchie NGM_LISTHOOKS, 49053913Sarchie "listhooks", 49153913Sarchie NULL, 49253913Sarchie &ng_generic_hooklist_type 49353913Sarchie }, 49453913Sarchie { 49553913Sarchie NGM_GENERIC_COOKIE, 49653913Sarchie NGM_LISTNAMES, 49753913Sarchie "listnames", 49853913Sarchie NULL, 49953913Sarchie &ng_generic_listnodes_type /* same as NGM_LISTNODES */ 50053913Sarchie }, 50153913Sarchie { 50253913Sarchie NGM_GENERIC_COOKIE, 50353913Sarchie NGM_LISTNODES, 50453913Sarchie "listnodes", 50553913Sarchie NULL, 50653913Sarchie &ng_generic_listnodes_type 50753913Sarchie }, 50853913Sarchie { 50953913Sarchie NGM_GENERIC_COOKIE, 51053913Sarchie NGM_LISTTYPES, 51153913Sarchie "listtypes", 51253913Sarchie NULL, 51353913Sarchie &ng_generic_typeinfo_type 51453913Sarchie }, 51553913Sarchie { 51653913Sarchie NGM_GENERIC_COOKIE, 51762471Sphk NGM_TEXT_CONFIG, 51862471Sphk "textconfig", 51962471Sphk NULL, 52062471Sphk &ng_parse_string_type 52162471Sphk }, 52262471Sphk { 52362471Sphk NGM_GENERIC_COOKIE, 52453913Sarchie NGM_TEXT_STATUS, 52553913Sarchie "textstatus", 52653913Sarchie NULL, 52753913Sarchie &ng_parse_string_type 52853913Sarchie }, 52953913Sarchie { 53053913Sarchie NGM_GENERIC_COOKIE, 53153913Sarchie NGM_ASCII2BINARY, 53253913Sarchie "ascii2binary", 53353913Sarchie &ng_parse_ng_mesg_type, 53453913Sarchie &ng_parse_ng_mesg_type 53553913Sarchie }, 53653913Sarchie { 53753913Sarchie NGM_GENERIC_COOKIE, 53853913Sarchie NGM_BINARY2ASCII, 53953913Sarchie "binary2ascii", 54053913Sarchie &ng_parse_ng_mesg_type, 54153913Sarchie &ng_parse_ng_mesg_type 54253913Sarchie }, 54353913Sarchie { 0 } 54453913Sarchie}; 54553913Sarchie 54653913Sarchie/************************************************************************ 54752419Sjulian Node routines 54852419Sjulian************************************************************************/ 54952419Sjulian 55052419Sjulian/* 55152419Sjulian * Instantiate a node of the requested type 55252419Sjulian */ 55352419Sjulianint 55452419Sjulianng_make_node(const char *typename, node_p *nodepp) 55552419Sjulian{ 55652419Sjulian struct ng_type *type; 55770700Sjulian int error; 55852419Sjulian 55952419Sjulian /* Check that the type makes sense */ 56052419Sjulian if (typename == NULL) { 56171047Sjulian TRAP_ERROR(); 56252419Sjulian return (EINVAL); 56352419Sjulian } 56452419Sjulian 565132705Sglebius /* Locate the node type. If we fail we return. Do not try to load 566132705Sglebius * module. 567132705Sglebius */ 568132705Sglebius if ((type = ng_findtype(typename)) == NULL) 569132705Sglebius return (ENXIO); 57052419Sjulian 57170700Sjulian /* 57270700Sjulian * If we have a constructor, then make the node and 57370700Sjulian * call the constructor to do type specific initialisation. 57470700Sjulian */ 57570700Sjulian if (type->constructor != NULL) { 57670700Sjulian if ((error = ng_make_node_common(type, nodepp)) == 0) { 57770700Sjulian if ((error = ((*type->constructor)(*nodepp)) != 0)) { 57870784Sjulian NG_NODE_UNREF(*nodepp); 57970700Sjulian } 58070700Sjulian } 58170700Sjulian } else { 58270700Sjulian /* 58370700Sjulian * Node has no constructor. We cannot ask for one 584167677Srwatson * to be made. It must be brought into existence by 58570935Sjulian * some external agency. The external agency should 58670700Sjulian * call ng_make_node_common() directly to get the 58770700Sjulian * netgraph part initialised. 58870700Sjulian */ 58971047Sjulian TRAP_ERROR(); 59070700Sjulian error = EINVAL; 59170700Sjulian } 59270700Sjulian return (error); 59352419Sjulian} 59452419Sjulian 59552419Sjulian/* 59670700Sjulian * Generic node creation. Called by node initialisation for externally 59770700Sjulian * instantiated nodes (e.g. hardware, sockets, etc ). 59852419Sjulian * The returned node has a reference count of 1. 59952419Sjulian */ 60052419Sjulianint 60152419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp) 60252419Sjulian{ 60352419Sjulian node_p node; 60452419Sjulian 60552419Sjulian /* Require the node type to have been already installed */ 60652419Sjulian if (ng_findtype(type->name) == NULL) { 60771047Sjulian TRAP_ERROR(); 60852419Sjulian return (EINVAL); 60952419Sjulian } 61052419Sjulian 61152419Sjulian /* Make a node and try attach it to the type */ 61270784Sjulian NG_ALLOC_NODE(node); 61352419Sjulian if (node == NULL) { 61471047Sjulian TRAP_ERROR(); 61552419Sjulian return (ENOMEM); 61652419Sjulian } 61770784Sjulian node->nd_type = type; 61870784Sjulian NG_NODE_REF(node); /* note reference */ 61952419Sjulian type->refs++; 62052419Sjulian 621168049Swkoszek NG_QUEUE_LOCK_INIT(&node->nd_input_queue); 62270784Sjulian node->nd_input_queue.queue = NULL; 62370784Sjulian node->nd_input_queue.last = &node->nd_input_queue.queue; 62470784Sjulian node->nd_input_queue.q_flags = 0; 62570784Sjulian node->nd_input_queue.q_node = node; 62652419Sjulian 62752419Sjulian /* Initialize hook list for new node */ 62870784Sjulian LIST_INIT(&node->nd_hooks); 62952419Sjulian 63070700Sjulian /* Link us into the node linked list */ 63172200Sbmilekic mtx_lock(&ng_nodelist_mtx); 63270784Sjulian LIST_INSERT_HEAD(&ng_nodelist, node, nd_nodes); 63372200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 63470700Sjulian 63570700Sjulian 63652722Sjulian /* get an ID and put us in the hash chain */ 63772200Sbmilekic mtx_lock(&ng_idhash_mtx); 63870784Sjulian for (;;) { /* wrap protection, even if silly */ 63970700Sjulian node_p node2 = NULL; 64070784Sjulian node->nd_ID = nextID++; /* 137/second for 1 year before wrap */ 64171354Sjulian 64270784Sjulian /* Is there a problem with the new number? */ 64371354Sjulian NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */ 64471354Sjulian if ((node->nd_ID != 0) && (node2 == NULL)) { 64570784Sjulian break; 64670700Sjulian } 64770784Sjulian } 64871354Sjulian LIST_INSERT_HEAD(&ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], 64970784Sjulian node, nd_idnodes); 65072200Sbmilekic mtx_unlock(&ng_idhash_mtx); 65152722Sjulian 65252419Sjulian /* Done */ 65352419Sjulian *nodepp = node; 65452419Sjulian return (0); 65552419Sjulian} 65652419Sjulian 65752419Sjulian/* 65852419Sjulian * Forceably start the shutdown process on a node. Either call 659167677Srwatson * its shutdown method, or do the default shutdown if there is 66052419Sjulian * no type-specific method. 66152419Sjulian * 662167677Srwatson * We can only be called from a shutdown message, so we know we have 66370939Sjulian * a writer lock, and therefore exclusive access. It also means 66470939Sjulian * that we should not be on the work queue, but we check anyhow. 66570700Sjulian * 66670700Sjulian * Persistent node types must have a type-specific method which 667167677Srwatson * allocates a new node in which case, this one is irretrievably going away, 66870939Sjulian * or cleans up anything it needs, and just makes the node valid again, 669152451Sglebius * in which case we allow the node to survive. 67070939Sjulian * 671167677Srwatson * XXX We need to think of how to tell a persistent node that we 67270939Sjulian * REALLY need to go away because the hardware has gone or we 67370939Sjulian * are rebooting.... etc. 67452419Sjulian */ 67552419Sjulianvoid 67671849Sjulianng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3) 67752419Sjulian{ 67870939Sjulian hook_p hook; 67970939Sjulian 68052419Sjulian /* Check if it's already shutting down */ 681132464Sjulian if ((node->nd_flags & NGF_CLOSING) != 0) 68252419Sjulian return; 68352419Sjulian 68471849Sjulian if (node == &ng_deadnode) { 68571849Sjulian printf ("shutdown called on deadnode\n"); 68671849Sjulian return; 68771849Sjulian } 68871849Sjulian 68952419Sjulian /* Add an extra reference so it doesn't go away during this */ 69070784Sjulian NG_NODE_REF(node); 69152419Sjulian 69270784Sjulian /* 69370784Sjulian * Mark it invalid so any newcomers know not to try use it 69470784Sjulian * Also add our own mark so we can't recurse 695132464Sjulian * note that NGF_INVALID does not do this as it's also set during 69670784Sjulian * creation 69770784Sjulian */ 698132464Sjulian node->nd_flags |= NGF_INVALID|NGF_CLOSING; 69952419Sjulian 700129836Sjulian /* If node has its pre-shutdown method, then call it first*/ 701129836Sjulian if (node->nd_type && node->nd_type->close) 702129836Sjulian (*node->nd_type->close)(node); 703129836Sjulian 70470939Sjulian /* Notify all remaining connected nodes to disconnect */ 70570939Sjulian while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL) 70670939Sjulian ng_destroy_hook(hook); 70770784Sjulian 70870700Sjulian /* 70970700Sjulian * Drain the input queue forceably. 71070784Sjulian * it has no hooks so what's it going to do, bleed on someone? 71170784Sjulian * Theoretically we came here from a queue entry that was added 71270784Sjulian * Just before the queue was closed, so it should be empty anyway. 71371902Sjulian * Also removes us from worklist if needed. 71470700Sjulian */ 71570784Sjulian ng_flush_input_queue(&node->nd_input_queue); 71670700Sjulian 71752419Sjulian /* Ask the type if it has anything to do in this case */ 71870784Sjulian if (node->nd_type && node->nd_type->shutdown) { 71970784Sjulian (*node->nd_type->shutdown)(node); 72071849Sjulian if (NG_NODE_IS_VALID(node)) { 72171849Sjulian /* 72271849Sjulian * Well, blow me down if the node code hasn't declared 72371849Sjulian * that it doesn't want to die. 72471849Sjulian * Presumably it is a persistant node. 72571849Sjulian * If we REALLY want it to go away, 72671849Sjulian * e.g. hardware going away, 727132464Sjulian * Our caller should set NGF_REALLY_DIE in nd_flags. 728152451Sglebius */ 729132464Sjulian node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING); 73071849Sjulian NG_NODE_UNREF(node); /* Assume they still have theirs */ 73171849Sjulian return; 73271849Sjulian } 73370700Sjulian } else { /* do the default thing */ 73470784Sjulian NG_NODE_UNREF(node); 73552419Sjulian } 73652419Sjulian 73770784Sjulian ng_unname(node); /* basically a NOP these days */ 73870784Sjulian 73970784Sjulian /* 74070784Sjulian * Remove extra reference, possibly the last 74170784Sjulian * Possible other holders of references may include 74270784Sjulian * timeout callouts, but theoretically the node's supposed to 74370784Sjulian * have cancelled them. Possibly hardware dependencies may 74470784Sjulian * force a driver to 'linger' with a reference. 74570784Sjulian */ 74670784Sjulian NG_NODE_UNREF(node); 74752419Sjulian} 74852419Sjulian 74952419Sjulian/* 75074078Sjulian * Remove a reference to the node, possibly the last. 75174078Sjulian * deadnode always acts as it it were the last. 75252419Sjulian */ 75374078Sjulianint 75470784Sjulianng_unref_node(node_p node) 75552419Sjulian{ 756152451Sglebius int v; 75771047Sjulian 75871047Sjulian if (node == &ng_deadnode) { 75974078Sjulian return (0); 76071047Sjulian } 76171047Sjulian 76270784Sjulian do { 76374078Sjulian v = node->nd_refs - 1; 76474078Sjulian } while (! atomic_cmpset_int(&node->nd_refs, v + 1, v)); 76569519Sjulian 76674078Sjulian if (v == 0) { /* we were the last */ 76770700Sjulian 76872200Sbmilekic mtx_lock(&ng_nodelist_mtx); 76970784Sjulian node->nd_type->refs--; /* XXX maybe should get types lock? */ 77070784Sjulian LIST_REMOVE(node, nd_nodes); 77172200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 77270700Sjulian 77372200Sbmilekic mtx_lock(&ng_idhash_mtx); 77470784Sjulian LIST_REMOVE(node, nd_idnodes); 77572200Sbmilekic mtx_unlock(&ng_idhash_mtx); 77652419Sjulian 77770791Sjulian mtx_destroy(&node->nd_input_queue.q_mtx); 77870700Sjulian NG_FREE_NODE(node); 77952419Sjulian } 78074078Sjulian return (v); 78152419Sjulian} 78252419Sjulian 78352419Sjulian/************************************************************************ 78452722Sjulian Node ID handling 78552722Sjulian************************************************************************/ 78652722Sjulianstatic node_p 78770700Sjulianng_ID2noderef(ng_ID_t ID) 78852722Sjulian{ 78970784Sjulian node_p node; 79072200Sbmilekic mtx_lock(&ng_idhash_mtx); 79171354Sjulian NG_IDHASH_FIND(ID, node); 79270784Sjulian if(node) 79370784Sjulian NG_NODE_REF(node); 79472200Sbmilekic mtx_unlock(&ng_idhash_mtx); 79570784Sjulian return(node); 79652722Sjulian} 79752722Sjulian 79852722Sjulianng_ID_t 79952722Sjulianng_node2ID(node_p node) 80052722Sjulian{ 80170912Sjulian return (node ? NG_NODE_ID(node) : 0); 80252722Sjulian} 80352722Sjulian 80452722Sjulian/************************************************************************ 80552419Sjulian Node name handling 80652419Sjulian************************************************************************/ 80752419Sjulian 80852419Sjulian/* 80952419Sjulian * Assign a node a name. Once assigned, the name cannot be changed. 81052419Sjulian */ 81152419Sjulianint 81252419Sjulianng_name_node(node_p node, const char *name) 81352419Sjulian{ 81452419Sjulian int i; 81570700Sjulian node_p node2; 81652419Sjulian 81752419Sjulian /* Check the name is valid */ 818125028Sharti for (i = 0; i < NG_NODESIZ; i++) { 81952419Sjulian if (name[i] == '\0' || name[i] == '.' || name[i] == ':') 82052419Sjulian break; 82152419Sjulian } 82252419Sjulian if (i == 0 || name[i] != '\0') { 82371047Sjulian TRAP_ERROR(); 82452419Sjulian return (EINVAL); 82552419Sjulian } 82652722Sjulian if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ 82771047Sjulian TRAP_ERROR(); 82852419Sjulian return (EINVAL); 82952419Sjulian } 83052419Sjulian 83152419Sjulian /* Check the name isn't already being used */ 83270700Sjulian if ((node2 = ng_name2noderef(node, name)) != NULL) { 83370784Sjulian NG_NODE_UNREF(node2); 83471047Sjulian TRAP_ERROR(); 83552419Sjulian return (EADDRINUSE); 83652419Sjulian } 83752419Sjulian 83870700Sjulian /* copy it */ 839125028Sharti strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ); 84052419Sjulian 84152419Sjulian return (0); 84252419Sjulian} 84352419Sjulian 84452419Sjulian/* 84552419Sjulian * Find a node by absolute name. The name should NOT end with ':' 84652419Sjulian * The name "." means "this node" and "[xxx]" means "the node 84752419Sjulian * with ID (ie, at address) xxx". 84852419Sjulian * 84952419Sjulian * Returns the node if found, else NULL. 85070700Sjulian * Eventually should add something faster than a sequential search. 851170035Srwatson * Note it acquires a reference on the node so you can be sure it's still 852170035Srwatson * there. 85352419Sjulian */ 85452419Sjuliannode_p 85570700Sjulianng_name2noderef(node_p here, const char *name) 85652419Sjulian{ 85752722Sjulian node_p node; 85852722Sjulian ng_ID_t temp; 85952419Sjulian 86052419Sjulian /* "." means "this node" */ 86170700Sjulian if (strcmp(name, ".") == 0) { 86270784Sjulian NG_NODE_REF(here); 86370700Sjulian return(here); 86470700Sjulian } 86552419Sjulian 86652419Sjulian /* Check for name-by-ID */ 86752722Sjulian if ((temp = ng_decodeidname(name)) != 0) { 86870700Sjulian return (ng_ID2noderef(temp)); 86952419Sjulian } 87052419Sjulian 87152419Sjulian /* Find node by name */ 87272200Sbmilekic mtx_lock(&ng_nodelist_mtx); 87370784Sjulian LIST_FOREACH(node, &ng_nodelist, nd_nodes) { 87470912Sjulian if (NG_NODE_IS_VALID(node) 87570912Sjulian && NG_NODE_HAS_NAME(node) 87670912Sjulian && (strcmp(NG_NODE_NAME(node), name) == 0)) { 87752419Sjulian break; 87870912Sjulian } 87952419Sjulian } 88070700Sjulian if (node) 88170784Sjulian NG_NODE_REF(node); 88272200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 88352419Sjulian return (node); 88452419Sjulian} 88552419Sjulian 88652419Sjulian/* 887108533Sschweikh * Decode an ID name, eg. "[f03034de]". Returns 0 if the 88852722Sjulian * string is not valid, otherwise returns the value. 88952419Sjulian */ 89052722Sjulianstatic ng_ID_t 89152419Sjulianng_decodeidname(const char *name) 89252419Sjulian{ 89352816Sarchie const int len = strlen(name); 89453648Sarchie char *eptr; 89552816Sarchie u_long val; 89652419Sjulian 89752816Sarchie /* Check for proper length, brackets, no leading junk */ 89870912Sjulian if ((len < 3) 89970912Sjulian || (name[0] != '[') 90070912Sjulian || (name[len - 1] != ']') 90170912Sjulian || (!isxdigit(name[1]))) { 90270912Sjulian return ((ng_ID_t)0); 90370912Sjulian } 90452419Sjulian 90552816Sarchie /* Decode number */ 90652816Sarchie val = strtoul(name + 1, &eptr, 16); 90770912Sjulian if ((eptr - name != len - 1) 90870912Sjulian || (val == ULONG_MAX) 90970912Sjulian || (val == 0)) { 91053042Sjulian return ((ng_ID_t)0); 91170912Sjulian } 91252816Sarchie return (ng_ID_t)val; 91352419Sjulian} 91452419Sjulian 91552419Sjulian/* 91652419Sjulian * Remove a name from a node. This should only be called 91752419Sjulian * when shutting down and removing the node. 918167677Srwatson * IF we allow name changing this may be more resurrected. 91952419Sjulian */ 92052419Sjulianvoid 92152419Sjulianng_unname(node_p node) 92252419Sjulian{ 92352419Sjulian} 92452419Sjulian 92552419Sjulian/************************************************************************ 92652419Sjulian Hook routines 92752419Sjulian Names are not optional. Hooks are always connected, except for a 92870939Sjulian brief moment within these routines. On invalidation or during creation 92970939Sjulian they are connected to the 'dead' hook. 93052419Sjulian************************************************************************/ 93152419Sjulian 93252419Sjulian/* 93352419Sjulian * Remove a hook reference 93452419Sjulian */ 93570784Sjulianvoid 93652419Sjulianng_unref_hook(hook_p hook) 93752419Sjulian{ 938152451Sglebius int v; 93971047Sjulian 94071047Sjulian if (hook == &ng_deadhook) { 94171047Sjulian return; 94271047Sjulian } 94370784Sjulian do { 94470784Sjulian v = hook->hk_refs; 94570784Sjulian } while (! atomic_cmpset_int(&hook->hk_refs, v, v - 1)); 94669519Sjulian 94770784Sjulian if (v == 1) { /* we were the last */ 94871047Sjulian if (_NG_HOOK_NODE(hook)) { /* it'll probably be ng_deadnode */ 94971047Sjulian _NG_NODE_UNREF((_NG_HOOK_NODE(hook))); 95070784Sjulian hook->hk_node = NULL; 95170700Sjulian } 95270700Sjulian NG_FREE_HOOK(hook); 95370700Sjulian } 95452419Sjulian} 95552419Sjulian 95652419Sjulian/* 95752419Sjulian * Add an unconnected hook to a node. Only used internally. 95870939Sjulian * Assumes node is locked. (XXX not yet true ) 95952419Sjulian */ 96052419Sjulianstatic int 96152419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp) 96252419Sjulian{ 96352419Sjulian hook_p hook; 96452419Sjulian int error = 0; 96552419Sjulian 96652419Sjulian /* Check that the given name is good */ 96752419Sjulian if (name == NULL) { 96871047Sjulian TRAP_ERROR(); 96952419Sjulian return (EINVAL); 97052419Sjulian } 97154096Sarchie if (ng_findhook(node, name) != NULL) { 97271047Sjulian TRAP_ERROR(); 97354096Sarchie return (EEXIST); 97452419Sjulian } 97552419Sjulian 97652419Sjulian /* Allocate the hook and link it up */ 97770784Sjulian NG_ALLOC_HOOK(hook); 97852419Sjulian if (hook == NULL) { 97971047Sjulian TRAP_ERROR(); 98052419Sjulian return (ENOMEM); 98152419Sjulian } 98270939Sjulian hook->hk_refs = 1; /* add a reference for us to return */ 98370784Sjulian hook->hk_flags = HK_INVALID; 98470939Sjulian hook->hk_peer = &ng_deadhook; /* start off this way */ 98570784Sjulian hook->hk_node = node; 98670784Sjulian NG_NODE_REF(node); /* each hook counts as a reference */ 98752419Sjulian 98870939Sjulian /* Set hook name */ 989125028Sharti strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ); 99070939Sjulian 99170939Sjulian /* 99270939Sjulian * Check if the node type code has something to say about it 99370939Sjulian * If it fails, the unref of the hook will also unref the node. 99470939Sjulian */ 99570935Sjulian if (node->nd_type->newhook != NULL) { 99670935Sjulian if ((error = (*node->nd_type->newhook)(node, hook, name))) { 99770784Sjulian NG_HOOK_UNREF(hook); /* this frees the hook */ 99870700Sjulian return (error); 99970700Sjulian } 100070935Sjulian } 100152419Sjulian /* 100252419Sjulian * The 'type' agrees so far, so go ahead and link it in. 100352419Sjulian * We'll ask again later when we actually connect the hooks. 100452419Sjulian */ 100570784Sjulian LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 100670784Sjulian node->nd_numhooks++; 100770939Sjulian NG_HOOK_REF(hook); /* one for the node */ 100852419Sjulian 100952419Sjulian if (hookp) 101052419Sjulian *hookp = hook; 101170939Sjulian return (0); 101252419Sjulian} 101352419Sjulian 101452419Sjulian/* 101554096Sarchie * Find a hook 101654096Sarchie * 101754096Sarchie * Node types may supply their own optimized routines for finding 101854096Sarchie * hooks. If none is supplied, we just do a linear search. 101970939Sjulian * XXX Possibly we should add a reference to the hook? 102054096Sarchie */ 102154096Sarchiehook_p 102254096Sarchieng_findhook(node_p node, const char *name) 102354096Sarchie{ 102454096Sarchie hook_p hook; 102554096Sarchie 102670784Sjulian if (node->nd_type->findhook != NULL) 102770784Sjulian return (*node->nd_type->findhook)(node, name); 102870784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 102970912Sjulian if (NG_HOOK_IS_VALID(hook) 103070917Sarchie && (strcmp(NG_HOOK_NAME(hook), name) == 0)) 103154096Sarchie return (hook); 103254096Sarchie } 103354096Sarchie return (NULL); 103454096Sarchie} 103554096Sarchie 103654096Sarchie/* 103752419Sjulian * Destroy a hook 103852419Sjulian * 103952419Sjulian * As hooks are always attached, this really destroys two hooks. 104052419Sjulian * The one given, and the one attached to it. Disconnect the hooks 104170939Sjulian * from each other first. We reconnect the peer hook to the 'dead' 104270939Sjulian * hook so that it can still exist after we depart. We then 104370939Sjulian * send the peer its own destroy message. This ensures that we only 1044152451Sglebius * interact with the peer's structures when it is locked processing that 104570939Sjulian * message. We hold a reference to the peer hook so we are guaranteed that 104670939Sjulian * the peer hook and node are still going to exist until 104770939Sjulian * we are finished there as the hook holds a ref on the node. 1048152451Sglebius * We run this same code again on the peer hook, but that time it is already 1049152451Sglebius * attached to the 'dead' hook. 105071047Sjulian * 1051152451Sglebius * This routine is called at all stages of hook creation 105271047Sjulian * on error detection and must be able to handle any such stage. 105352419Sjulian */ 105452419Sjulianvoid 105552419Sjulianng_destroy_hook(hook_p hook) 105652419Sjulian{ 1057151974Sglebius hook_p peer; 1058151974Sglebius node_p node; 105952419Sjulian 106071047Sjulian if (hook == &ng_deadhook) { /* better safe than sorry */ 106171047Sjulian printf("ng_destroy_hook called on deadhook\n"); 106271047Sjulian return; 106371047Sjulian } 1064151974Sglebius 1065151974Sglebius /* 1066151974Sglebius * Protect divorce process with mutex, to avoid races on 1067151974Sglebius * simultaneous disconnect. 1068151974Sglebius */ 1069151974Sglebius mtx_lock(&ng_topo_mtx); 1070151974Sglebius 1071151974Sglebius hook->hk_flags |= HK_INVALID; 1072151974Sglebius 1073151974Sglebius peer = NG_HOOK_PEER(hook); 1074151974Sglebius node = NG_HOOK_NODE(hook); 1075151974Sglebius 107670939Sjulian if (peer && (peer != &ng_deadhook)) { 107770939Sjulian /* 107870939Sjulian * Set the peer to point to ng_deadhook 107970939Sjulian * from this moment on we are effectively independent it. 108070939Sjulian * send it an rmhook message of it's own. 108170939Sjulian */ 108270939Sjulian peer->hk_peer = &ng_deadhook; /* They no longer know us */ 108370939Sjulian hook->hk_peer = &ng_deadhook; /* Nor us, them */ 108471047Sjulian if (NG_HOOK_NODE(peer) == &ng_deadnode) { 1085152451Sglebius /* 108671047Sjulian * If it's already divorced from a node, 108771047Sjulian * just free it. 108871047Sjulian */ 1089151974Sglebius mtx_unlock(&ng_topo_mtx); 109071047Sjulian } else { 1091151974Sglebius mtx_unlock(&ng_topo_mtx); 109271047Sjulian ng_rmhook_self(peer); /* Send it a surprise */ 109371047Sjulian } 109470942Sjulian NG_HOOK_UNREF(peer); /* account for peer link */ 109570942Sjulian NG_HOOK_UNREF(hook); /* account for peer link */ 1096151974Sglebius } else 1097151974Sglebius mtx_unlock(&ng_topo_mtx); 109852419Sjulian 1099151974Sglebius mtx_assert(&ng_topo_mtx, MA_NOTOWNED); 1100151974Sglebius 110152419Sjulian /* 110252419Sjulian * Remove the hook from the node's list to avoid possible recursion 110352419Sjulian * in case the disconnection results in node shutdown. 110452419Sjulian */ 110571047Sjulian if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */ 110671047Sjulian return; 110771047Sjulian } 110870784Sjulian LIST_REMOVE(hook, hk_hooks); 110970784Sjulian node->nd_numhooks--; 111070784Sjulian if (node->nd_type->disconnect) { 111152419Sjulian /* 111271047Sjulian * The type handler may elect to destroy the node so don't 1113167677Srwatson * trust its existence after this point. (except 111471047Sjulian * that we still hold a reference on it. (which we 111571047Sjulian * inherrited from the hook we are destroying) 111652419Sjulian */ 111770784Sjulian (*node->nd_type->disconnect) (hook); 111852419Sjulian } 111971047Sjulian 112071047Sjulian /* 112171047Sjulian * Note that because we will point to ng_deadnode, the original node 112271047Sjulian * is not decremented automatically so we do that manually. 112371047Sjulian */ 112471047Sjulian _NG_HOOK_NODE(hook) = &ng_deadnode; 112571047Sjulian NG_NODE_UNREF(node); /* We no longer point to it so adjust count */ 112671047Sjulian NG_HOOK_UNREF(hook); /* Account for linkage (in list) to node */ 112752419Sjulian} 112852419Sjulian 112952419Sjulian/* 113052419Sjulian * Take two hooks on a node and merge the connection so that the given node 113152419Sjulian * is effectively bypassed. 113252419Sjulian */ 113352419Sjulianint 113452419Sjulianng_bypass(hook_p hook1, hook_p hook2) 113552419Sjulian{ 113670784Sjulian if (hook1->hk_node != hook2->hk_node) { 113771047Sjulian TRAP_ERROR(); 113852419Sjulian return (EINVAL); 113970784Sjulian } 114070784Sjulian hook1->hk_peer->hk_peer = hook2->hk_peer; 114170784Sjulian hook2->hk_peer->hk_peer = hook1->hk_peer; 114252419Sjulian 114370939Sjulian hook1->hk_peer = &ng_deadhook; 114470939Sjulian hook2->hk_peer = &ng_deadhook; 114570939Sjulian 1146163244Sglebius NG_HOOK_UNREF(hook1); 1147163244Sglebius NG_HOOK_UNREF(hook2); 1148163244Sglebius 114952419Sjulian /* XXX If we ever cache methods on hooks update them as well */ 115052419Sjulian ng_destroy_hook(hook1); 115152419Sjulian ng_destroy_hook(hook2); 115252419Sjulian return (0); 115352419Sjulian} 115452419Sjulian 115552419Sjulian/* 115652419Sjulian * Install a new netgraph type 115752419Sjulian */ 115852419Sjulianint 115952419Sjulianng_newtype(struct ng_type *tp) 116052419Sjulian{ 116152419Sjulian const size_t namelen = strlen(tp->name); 116252419Sjulian 116352419Sjulian /* Check version and type name fields */ 116470159Sjulian if ((tp->version != NG_ABI_VERSION) 116570159Sjulian || (namelen == 0) 1166125028Sharti || (namelen >= NG_TYPESIZ)) { 116771047Sjulian TRAP_ERROR(); 1168131374Sjulian if (tp->version != NG_ABI_VERSION) { 1169131374Sjulian printf("Netgraph: Node type rejected. ABI mismatch. Suggest recompile\n"); 1170131374Sjulian } 117152419Sjulian return (EINVAL); 117252419Sjulian } 117352419Sjulian 117452419Sjulian /* Check for name collision */ 117552419Sjulian if (ng_findtype(tp->name) != NULL) { 117671047Sjulian TRAP_ERROR(); 117752419Sjulian return (EEXIST); 117852419Sjulian } 117952419Sjulian 118070700Sjulian 118152419Sjulian /* Link in new type */ 118272200Sbmilekic mtx_lock(&ng_typelist_mtx); 118370700Sjulian LIST_INSERT_HEAD(&ng_typelist, tp, types); 118471603Sjulian tp->refs = 1; /* first ref is linked list */ 118572200Sbmilekic mtx_unlock(&ng_typelist_mtx); 118652419Sjulian return (0); 118752419Sjulian} 118852419Sjulian 118952419Sjulian/* 119080222Sjulian * unlink a netgraph type 119180222Sjulian * If no examples exist 119280222Sjulian */ 119380222Sjulianint 119480222Sjulianng_rmtype(struct ng_type *tp) 119580222Sjulian{ 119680222Sjulian /* Check for name collision */ 119780222Sjulian if (tp->refs != 1) { 119880222Sjulian TRAP_ERROR(); 119980222Sjulian return (EBUSY); 120080222Sjulian } 120180222Sjulian 120280222Sjulian /* Unlink type */ 120380222Sjulian mtx_lock(&ng_typelist_mtx); 120480222Sjulian LIST_REMOVE(tp, types); 120580222Sjulian mtx_unlock(&ng_typelist_mtx); 120680222Sjulian return (0); 120780222Sjulian} 120880222Sjulian 120980222Sjulian/* 121052419Sjulian * Look for a type of the name given 121152419Sjulian */ 121252419Sjulianstruct ng_type * 121352419Sjulianng_findtype(const char *typename) 121452419Sjulian{ 121552419Sjulian struct ng_type *type; 121652419Sjulian 121772200Sbmilekic mtx_lock(&ng_typelist_mtx); 121870700Sjulian LIST_FOREACH(type, &ng_typelist, types) { 121952419Sjulian if (strcmp(type->name, typename) == 0) 122052419Sjulian break; 122152419Sjulian } 122272200Sbmilekic mtx_unlock(&ng_typelist_mtx); 122352419Sjulian return (type); 122452419Sjulian} 122552419Sjulian 122652419Sjulian/************************************************************************ 122752419Sjulian Composite routines 122852419Sjulian************************************************************************/ 122952419Sjulian/* 123071047Sjulian * Connect two nodes using the specified hooks, using queued functions. 123152419Sjulian */ 123271849Sjulianstatic void 123371047Sjulianng_con_part3(node_p node, hook_p hook, void *arg1, int arg2) 123452419Sjulian{ 123552419Sjulian 123671047Sjulian /* 123771047Sjulian * When we run, we know that the node 'node' is locked for us. 123871047Sjulian * Our caller has a reference on the hook. 123971047Sjulian * Our caller has a reference on the node. 124071047Sjulian * (In this case our caller is ng_apply_item() ). 124171047Sjulian * The peer hook has a reference on the hook. 124271849Sjulian * We are all set up except for the final call to the node, and 124371849Sjulian * the clearing of the INVALID flag. 124471047Sjulian */ 124571047Sjulian if (NG_HOOK_NODE(hook) == &ng_deadnode) { 124671047Sjulian /* 124771047Sjulian * The node must have been freed again since we last visited 124871047Sjulian * here. ng_destry_hook() has this effect but nothing else does. 124971047Sjulian * We should just release our references and 125071047Sjulian * free anything we can think of. 125171047Sjulian * Since we know it's been destroyed, and it's our caller 125271047Sjulian * that holds the references, just return. 125371047Sjulian */ 125471849Sjulian return ; 125552419Sjulian } 125671047Sjulian if (hook->hk_node->nd_type->connect) { 125771849Sjulian if ((*hook->hk_node->nd_type->connect) (hook)) { 125871047Sjulian ng_destroy_hook(hook); /* also zaps peer */ 125971849Sjulian printf("failed in ng_con_part3()\n"); 126071849Sjulian return ; 126171047Sjulian } 126252419Sjulian } 126371047Sjulian /* 126471047Sjulian * XXX this is wrong for SMP. Possibly we need 126571047Sjulian * to separate out 'create' and 'invalid' flags. 126671047Sjulian * should only set flags on hooks we have locked under our node. 126771047Sjulian */ 126871047Sjulian hook->hk_flags &= ~HK_INVALID; 126971849Sjulian return ; 127071047Sjulian} 127152419Sjulian 127271849Sjulianstatic void 127371047Sjulianng_con_part2(node_p node, hook_p hook, void *arg1, int arg2) 127471047Sjulian{ 1275151974Sglebius hook_p peer; 127671047Sjulian 127752419Sjulian /* 127871047Sjulian * When we run, we know that the node 'node' is locked for us. 127971047Sjulian * Our caller has a reference on the hook. 128071047Sjulian * Our caller has a reference on the node. 128171047Sjulian * (In this case our caller is ng_apply_item() ). 128271047Sjulian * The peer hook has a reference on the hook. 128371047Sjulian * our node pointer points to the 'dead' node. 128471047Sjulian * First check the hook name is unique. 128571849Sjulian * Should not happen because we checked before queueing this. 128652419Sjulian */ 128771047Sjulian if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) { 128871047Sjulian TRAP_ERROR(); 128971047Sjulian ng_destroy_hook(hook); /* should destroy peer too */ 129071849Sjulian printf("failed in ng_con_part2()\n"); 129171849Sjulian return ; 129271047Sjulian } 129370939Sjulian /* 129471047Sjulian * Check if the node type code has something to say about it 129571047Sjulian * If it fails, the unref of the hook will also unref the attached node, 129671047Sjulian * however since that node is 'ng_deadnode' this will do nothing. 129771047Sjulian * The peer hook will also be destroyed. 129870939Sjulian */ 129971047Sjulian if (node->nd_type->newhook != NULL) { 130071849Sjulian if ((*node->nd_type->newhook)(node, hook, hook->hk_name)) { 130171047Sjulian ng_destroy_hook(hook); /* should destroy peer too */ 130271849Sjulian printf("failed in ng_con_part2()\n"); 130371849Sjulian return ; 130471047Sjulian } 130571047Sjulian } 130671047Sjulian 130771047Sjulian /* 130871047Sjulian * The 'type' agrees so far, so go ahead and link it in. 130971047Sjulian * We'll ask again later when we actually connect the hooks. 131071047Sjulian */ 131171047Sjulian hook->hk_node = node; /* just overwrite ng_deadnode */ 131271047Sjulian NG_NODE_REF(node); /* each hook counts as a reference */ 131371047Sjulian LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 131471047Sjulian node->nd_numhooks++; 131571047Sjulian NG_HOOK_REF(hook); /* one for the node */ 131671047Sjulian 131771047Sjulian /* 1318167677Srwatson * We now have a symmetrical situation, where both hooks have been 131974078Sjulian * linked to their nodes, the newhook methods have been called 132071047Sjulian * And the references are all correct. The hooks are still marked 132171047Sjulian * as invalid, as we have not called the 'connect' methods 132271047Sjulian * yet. 1323167677Srwatson * We can call the local one immediately as we have the 132471047Sjulian * node locked, but we need to queue the remote one. 132571047Sjulian */ 132671047Sjulian if (hook->hk_node->nd_type->connect) { 132771849Sjulian if ((*hook->hk_node->nd_type->connect) (hook)) { 132871047Sjulian ng_destroy_hook(hook); /* also zaps peer */ 132971849Sjulian printf("failed in ng_con_part2(A)\n"); 133071849Sjulian return ; 133171047Sjulian } 133271047Sjulian } 1333151974Sglebius 1334151974Sglebius /* 1335151974Sglebius * Acquire topo mutex to avoid race with ng_destroy_hook(). 1336151974Sglebius */ 1337151974Sglebius mtx_lock(&ng_topo_mtx); 1338151974Sglebius peer = hook->hk_peer; 1339151974Sglebius if (peer == &ng_deadhook) { 1340151974Sglebius mtx_unlock(&ng_topo_mtx); 1341151974Sglebius printf("failed in ng_con_part2(B)\n"); 1342151974Sglebius ng_destroy_hook(hook); 1343151974Sglebius return ; 1344151974Sglebius } 1345151974Sglebius mtx_unlock(&ng_topo_mtx); 1346151974Sglebius 1347151974Sglebius if (ng_send_fn(peer->hk_node, peer, &ng_con_part3, arg1, arg2)) { 1348151974Sglebius printf("failed in ng_con_part2(C)\n"); 134971849Sjulian ng_destroy_hook(hook); /* also zaps peer */ 135071849Sjulian return ; 135171849Sjulian } 135271047Sjulian hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */ 135371849Sjulian return ; 135452419Sjulian} 135552419Sjulian 135652419Sjulian/* 1357152451Sglebius * Connect this node with another node. We assume that this node is 135871047Sjulian * currently locked, as we are only called from an NGM_CONNECT message. 135952419Sjulian */ 136071047Sjulianstatic int 136152419Sjulianng_con_nodes(node_p node, const char *name, node_p node2, const char *name2) 136252419Sjulian{ 1363152451Sglebius int error; 1364152451Sglebius hook_p hook; 1365152451Sglebius hook_p hook2; 136652419Sjulian 136771849Sjulian if (ng_findhook(node2, name2) != NULL) { 136871849Sjulian return(EEXIST); 136971849Sjulian } 137070939Sjulian if ((error = ng_add_hook(node, name, &hook))) /* gives us a ref */ 137152419Sjulian return (error); 137271047Sjulian /* Allocate the other hook and link it up */ 137371047Sjulian NG_ALLOC_HOOK(hook2); 1374140737Sglebius if (hook2 == NULL) { 137571047Sjulian TRAP_ERROR(); 137671047Sjulian ng_destroy_hook(hook); /* XXX check ref counts so far */ 137771047Sjulian NG_HOOK_UNREF(hook); /* including our ref */ 137871047Sjulian return (ENOMEM); 137971047Sjulian } 138071047Sjulian hook2->hk_refs = 1; /* start with a reference for us. */ 138171047Sjulian hook2->hk_flags = HK_INVALID; 138271047Sjulian hook2->hk_peer = hook; /* Link the two together */ 138371047Sjulian hook->hk_peer = hook2; 138471047Sjulian NG_HOOK_REF(hook); /* Add a ref for the peer to each*/ 138571047Sjulian NG_HOOK_REF(hook2); 1386152451Sglebius hook2->hk_node = &ng_deadnode; 1387125028Sharti strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ); 138871047Sjulian 138971047Sjulian /* 139071047Sjulian * Queue the function above. 139171047Sjulian * Procesing continues in that function in the lock context of 139271047Sjulian * the other node. 139371047Sjulian */ 1394171885Smav if ((error = ng_send_fn(node2, hook2, &ng_con_part2, NULL, 0))) { 1395171885Smav printf("failed in ng_con_nodes(): %d\n", error); 1396171885Smav ng_destroy_hook(hook); /* also zaps peer */ 1397171885Smav } 139871047Sjulian 139971047Sjulian NG_HOOK_UNREF(hook); /* Let each hook go if it wants to */ 140071047Sjulian NG_HOOK_UNREF(hook2); 1401171885Smav return (error); 140271047Sjulian} 140371047Sjulian 140471047Sjulian/* 140571047Sjulian * Make a peer and connect. 140671047Sjulian * We assume that the local node is locked. 140771047Sjulian * The new node probably doesn't need a lock until 140871047Sjulian * it has a hook, because it cannot really have any work until then, 140971047Sjulian * but we should think about it a bit more. 141071047Sjulian * 141171047Sjulian * The problem may come if the other node also fires up 141271047Sjulian * some hardware or a timer or some other source of activation, 141371047Sjulian * also it may already get a command msg via it's ID. 141471047Sjulian * 141571047Sjulian * We could use the same method as ng_con_nodes() but we'd have 1416152451Sglebius * to add ability to remove the node when failing. (Not hard, just 141771047Sjulian * make arg1 point to the node to remove). 141871047Sjulian * Unless of course we just ignore failure to connect and leave 141971047Sjulian * an unconnected node? 142071047Sjulian */ 142171047Sjulianstatic int 142271047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type) 142371047Sjulian{ 1424152451Sglebius node_p node2; 1425152451Sglebius hook_p hook1, hook2; 1426152451Sglebius int error; 142771047Sjulian 142871047Sjulian if ((error = ng_make_node(type, &node2))) { 142952419Sjulian return (error); 143052419Sjulian } 143170939Sjulian 143271047Sjulian if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */ 143371849Sjulian ng_rmnode(node2, NULL, NULL, 0); 143471047Sjulian return (error); 143571047Sjulian } 143671047Sjulian 143771047Sjulian if ((error = ng_add_hook(node2, name2, &hook2))) { 143871849Sjulian ng_rmnode(node2, NULL, NULL, 0); 143971047Sjulian ng_destroy_hook(hook1); 144071047Sjulian NG_HOOK_UNREF(hook1); 144171047Sjulian return (error); 144271047Sjulian } 144371047Sjulian 144470939Sjulian /* 144571047Sjulian * Actually link the two hooks together. 144671047Sjulian */ 144771047Sjulian hook1->hk_peer = hook2; 144871047Sjulian hook2->hk_peer = hook1; 144971047Sjulian 145071047Sjulian /* Each hook is referenced by the other */ 145171047Sjulian NG_HOOK_REF(hook1); 145271047Sjulian NG_HOOK_REF(hook2); 145371047Sjulian 145471047Sjulian /* Give each node the opportunity to veto the pending connection */ 145571047Sjulian if (hook1->hk_node->nd_type->connect) { 145671047Sjulian error = (*hook1->hk_node->nd_type->connect) (hook1); 145771047Sjulian } 145871047Sjulian 145971047Sjulian if ((error == 0) && hook2->hk_node->nd_type->connect) { 146071047Sjulian error = (*hook2->hk_node->nd_type->connect) (hook2); 146171047Sjulian 146271047Sjulian } 146371047Sjulian 146471047Sjulian /* 146570939Sjulian * drop the references we were holding on the two hooks. 146670939Sjulian */ 146771047Sjulian if (error) { 146871047Sjulian ng_destroy_hook(hook2); /* also zaps hook1 */ 146971849Sjulian ng_rmnode(node2, NULL, NULL, 0); 147071047Sjulian } else { 147171047Sjulian /* As a last act, allow the hooks to be used */ 147271047Sjulian hook1->hk_flags &= ~HK_INVALID; 147371047Sjulian hook2->hk_flags &= ~HK_INVALID; 147471047Sjulian } 147571047Sjulian NG_HOOK_UNREF(hook1); 147670939Sjulian NG_HOOK_UNREF(hook2); 147770939Sjulian return (error); 147852419Sjulian} 147971047Sjulian 148070700Sjulian/************************************************************************ 148170700Sjulian Utility routines to send self messages 148270700Sjulian************************************************************************/ 148370700Sjulian 148471849Sjulian/* Shut this node down as soon as everyone is clear of it */ 1485167677Srwatson/* Should add arg "immediately" to jump the queue */ 148670700Sjulianint 148771849Sjulianng_rmnode_self(node_p node) 148870700Sjulian{ 148971849Sjulian int error; 149052419Sjulian 149171849Sjulian if (node == &ng_deadnode) 149271849Sjulian return (0); 1493132464Sjulian node->nd_flags |= NGF_INVALID; 1494132464Sjulian if (node->nd_flags & NGF_CLOSING) 149571849Sjulian return (0); 149670700Sjulian 149771849Sjulian error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0); 149871849Sjulian return (error); 149970700Sjulian} 150070700Sjulian 150171849Sjulianstatic void 150271047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2) 150371047Sjulian{ 150471047Sjulian ng_destroy_hook(hook); 150571849Sjulian return ; 150671047Sjulian} 150771047Sjulian 150870935Sjulianint 150970935Sjulianng_rmhook_self(hook_p hook) 151070935Sjulian{ 151171047Sjulian int error; 151270935Sjulian node_p node = NG_HOOK_NODE(hook); 151370935Sjulian 151471047Sjulian if (node == &ng_deadnode) 151571047Sjulian return (0); 151671047Sjulian 151771047Sjulian error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0); 151871047Sjulian return (error); 151970935Sjulian} 152070935Sjulian 152170700Sjulian/*********************************************************************** 152252419Sjulian * Parse and verify a string of the form: <NODE:><PATH> 152352419Sjulian * 152452419Sjulian * Such a string can refer to a specific node or a specific hook 152552419Sjulian * on a specific node, depending on how you look at it. In the 152652419Sjulian * latter case, the PATH component must not end in a dot. 152752419Sjulian * 152852419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string 152952419Sjulian * of hook names separated by dots. This breaks out the original 153052419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp 153152419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to 153252419Sjulian * the final hook component of <PATH>, if any, otherwise NULL. 153352419Sjulian * 153452419Sjulian * This returns -1 if the path is malformed. The char ** are optional. 153570700Sjulian ***********************************************************************/ 153652419Sjulianint 153752419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 153852419Sjulian{ 1539152451Sglebius char *node, *path, *hook; 1540152451Sglebius int k; 154152419Sjulian 154252419Sjulian /* 154352419Sjulian * Extract absolute NODE, if any 154452419Sjulian */ 154552419Sjulian for (path = addr; *path && *path != ':'; path++); 154652419Sjulian if (*path) { 154752419Sjulian node = addr; /* Here's the NODE */ 154852419Sjulian *path++ = '\0'; /* Here's the PATH */ 154952419Sjulian 155052419Sjulian /* Node name must not be empty */ 155152419Sjulian if (!*node) 155252419Sjulian return -1; 155352419Sjulian 155452419Sjulian /* A name of "." is OK; otherwise '.' not allowed */ 155552419Sjulian if (strcmp(node, ".") != 0) { 155652419Sjulian for (k = 0; node[k]; k++) 155752419Sjulian if (node[k] == '.') 155852419Sjulian return -1; 155952419Sjulian } 156052419Sjulian } else { 156152419Sjulian node = NULL; /* No absolute NODE */ 156252419Sjulian path = addr; /* Here's the PATH */ 156352419Sjulian } 156452419Sjulian 156552419Sjulian /* Snoop for illegal characters in PATH */ 156652419Sjulian for (k = 0; path[k]; k++) 156752419Sjulian if (path[k] == ':') 156852419Sjulian return -1; 156952419Sjulian 157052419Sjulian /* Check for no repeated dots in PATH */ 157152419Sjulian for (k = 0; path[k]; k++) 157252419Sjulian if (path[k] == '.' && path[k + 1] == '.') 157352419Sjulian return -1; 157452419Sjulian 157552419Sjulian /* Remove extra (degenerate) dots from beginning or end of PATH */ 157652419Sjulian if (path[0] == '.') 157752419Sjulian path++; 157852419Sjulian if (*path && path[strlen(path) - 1] == '.') 157952419Sjulian path[strlen(path) - 1] = 0; 158052419Sjulian 158152419Sjulian /* If PATH has a dot, then we're not talking about a hook */ 158252419Sjulian if (*path) { 158352419Sjulian for (hook = path, k = 0; path[k]; k++) 158452419Sjulian if (path[k] == '.') { 158552419Sjulian hook = NULL; 158652419Sjulian break; 158752419Sjulian } 158852419Sjulian } else 158952419Sjulian path = hook = NULL; 159052419Sjulian 159152419Sjulian /* Done */ 159252419Sjulian if (nodep) 159352419Sjulian *nodep = node; 159452419Sjulian if (pathp) 159552419Sjulian *pathp = path; 159652419Sjulian if (hookp) 159752419Sjulian *hookp = hook; 159852419Sjulian return (0); 159952419Sjulian} 160052419Sjulian 160152419Sjulian/* 160252419Sjulian * Given a path, which may be absolute or relative, and a starting node, 160370700Sjulian * return the destination node. 160452419Sjulian */ 160552419Sjulianint 160670700Sjulianng_path2noderef(node_p here, const char *address, 160770700Sjulian node_p *destp, hook_p *lasthook) 160852419Sjulian{ 1609125028Sharti char fullpath[NG_PATHSIZ]; 161052419Sjulian char *nodename, *path, pbuf[2]; 161170700Sjulian node_p node, oldnode; 161252419Sjulian char *cp; 161359728Sjulian hook_p hook = NULL; 161452419Sjulian 161552419Sjulian /* Initialize */ 161670784Sjulian if (destp == NULL) { 161771047Sjulian TRAP_ERROR(); 161852419Sjulian return EINVAL; 161970784Sjulian } 162052419Sjulian *destp = NULL; 162152419Sjulian 162252419Sjulian /* Make a writable copy of address for ng_path_parse() */ 162352419Sjulian strncpy(fullpath, address, sizeof(fullpath) - 1); 162452419Sjulian fullpath[sizeof(fullpath) - 1] = '\0'; 162552419Sjulian 162652419Sjulian /* Parse out node and sequence of hooks */ 162752419Sjulian if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 162871047Sjulian TRAP_ERROR(); 162952419Sjulian return EINVAL; 163052419Sjulian } 163152419Sjulian if (path == NULL) { 163252419Sjulian pbuf[0] = '.'; /* Needs to be writable */ 163352419Sjulian pbuf[1] = '\0'; 163452419Sjulian path = pbuf; 163552419Sjulian } 163652419Sjulian 163770700Sjulian /* 163870700Sjulian * For an absolute address, jump to the starting node. 163970700Sjulian * Note that this holds a reference on the node for us. 164070700Sjulian * Don't forget to drop the reference if we don't need it. 164170700Sjulian */ 164252419Sjulian if (nodename) { 164370700Sjulian node = ng_name2noderef(here, nodename); 164452419Sjulian if (node == NULL) { 164571047Sjulian TRAP_ERROR(); 164652419Sjulian return (ENOENT); 164752419Sjulian } 164870700Sjulian } else { 164970700Sjulian if (here == NULL) { 165071047Sjulian TRAP_ERROR(); 165170700Sjulian return (EINVAL); 165270700Sjulian } 165352419Sjulian node = here; 165470784Sjulian NG_NODE_REF(node); 165570700Sjulian } 165652419Sjulian 165770700Sjulian /* 1658152451Sglebius * Now follow the sequence of hooks 165970700Sjulian * XXX 166070700Sjulian * We actually cannot guarantee that the sequence 166170700Sjulian * is not being demolished as we crawl along it 166270700Sjulian * without extra-ordinary locking etc. 166370700Sjulian * So this is a bit dodgy to say the least. 166470700Sjulian * We can probably hold up some things by holding 166570700Sjulian * the nodelist mutex for the time of this 166670700Sjulian * crawl if we wanted.. At least that way we wouldn't have to 1667167677Srwatson * worry about the nodes disappearing, but the hooks would still 166870700Sjulian * be a problem. 166970700Sjulian */ 167052419Sjulian for (cp = path; node != NULL && *cp != '\0'; ) { 167152419Sjulian char *segment; 167252419Sjulian 167352419Sjulian /* 167452419Sjulian * Break out the next path segment. Replace the dot we just 167552419Sjulian * found with a NUL; "cp" points to the next segment (or the 167652419Sjulian * NUL at the end). 167752419Sjulian */ 167852419Sjulian for (segment = cp; *cp != '\0'; cp++) { 167952419Sjulian if (*cp == '.') { 168052419Sjulian *cp++ = '\0'; 168152419Sjulian break; 168252419Sjulian } 168352419Sjulian } 168452419Sjulian 168552419Sjulian /* Empty segment */ 168652419Sjulian if (*segment == '\0') 168752419Sjulian continue; 168852419Sjulian 168952419Sjulian /* We have a segment, so look for a hook by that name */ 169054096Sarchie hook = ng_findhook(node, segment); 169152419Sjulian 169252419Sjulian /* Can't get there from here... */ 169352419Sjulian if (hook == NULL 169470784Sjulian || NG_HOOK_PEER(hook) == NULL 169570784Sjulian || NG_HOOK_NOT_VALID(hook) 169670784Sjulian || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) { 169771047Sjulian TRAP_ERROR(); 169870784Sjulian NG_NODE_UNREF(node); 1699152451Sglebius#if 0 170070784Sjulian printf("hooknotvalid %s %s %d %d %d %d ", 170170784Sjulian path, 170270784Sjulian segment, 170370784Sjulian hook == NULL, 1704152451Sglebius NG_HOOK_PEER(hook) == NULL, 1705152451Sglebius NG_HOOK_NOT_VALID(hook), 1706152451Sglebius NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))); 170770784Sjulian#endif 170852419Sjulian return (ENOENT); 170952419Sjulian } 171052419Sjulian 171170700Sjulian /* 1712152451Sglebius * Hop on over to the next node 171370700Sjulian * XXX 1714152451Sglebius * Big race conditions here as hooks and nodes go away 171570700Sjulian * *** Idea.. store an ng_ID_t in each hook and use that 171670700Sjulian * instead of the direct hook in this crawl? 171770700Sjulian */ 171870700Sjulian oldnode = node; 171970784Sjulian if ((node = NG_PEER_NODE(hook))) 172070784Sjulian NG_NODE_REF(node); /* XXX RACE */ 172170784Sjulian NG_NODE_UNREF(oldnode); /* XXX another race */ 172270784Sjulian if (NG_NODE_NOT_VALID(node)) { 172370784Sjulian NG_NODE_UNREF(node); /* XXX more races */ 172470700Sjulian node = NULL; 172570700Sjulian } 172652419Sjulian } 172752419Sjulian 172852419Sjulian /* If node somehow missing, fail here (probably this is not needed) */ 172952419Sjulian if (node == NULL) { 173071047Sjulian TRAP_ERROR(); 173152419Sjulian return (ENXIO); 173252419Sjulian } 173352419Sjulian 173452419Sjulian /* Done */ 173552419Sjulian *destp = node; 173659900Sarchie if (lasthook != NULL) 173770784Sjulian *lasthook = (hook ? NG_HOOK_PEER(hook) : NULL); 173852419Sjulian return (0); 173952419Sjulian} 174052419Sjulian 174170700Sjulian/***************************************************************\ 174270700Sjulian* Input queue handling. 174370700Sjulian* All activities are submitted to the node via the input queue 174470700Sjulian* which implements a multiple-reader/single-writer gate. 1745167677Srwatson* Items which cannot be handled immediately are queued. 174670700Sjulian* 174770700Sjulian* read-write queue locking inline functions * 174870700Sjulian\***************************************************************/ 174970700Sjulian 1750151238Sglebiusstatic __inline item_p ng_dequeue(struct ng_queue * ngq, int *rw); 175170700Sjulianstatic __inline item_p ng_acquire_read(struct ng_queue * ngq, 175270700Sjulian item_p item); 175370700Sjulianstatic __inline item_p ng_acquire_write(struct ng_queue * ngq, 175470700Sjulian item_p item); 175570700Sjulianstatic __inline void ng_leave_read(struct ng_queue * ngq); 175670700Sjulianstatic __inline void ng_leave_write(struct ng_queue * ngq); 175770700Sjulianstatic __inline void ng_queue_rw(struct ng_queue * ngq, 175870700Sjulian item_p item, int rw); 175970700Sjulian 176052419Sjulian/* 176170700Sjulian * Definition of the bits fields in the ng_queue flag word. 176270700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle 176370700Sjulian * with them. 176470700Sjulian * 176571902Sjulian * The ordering here may be important! don't shuffle these. 176652419Sjulian */ 176770700Sjulian/*- 176870700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet) 176970700Sjulian | 177070700Sjulian V 177170700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+ 1772151973Sglebius | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1773151973Sglebius | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A| 1774151973Sglebius | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W| 177570700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+ 1776151973Sglebius \___________________________ ____________________________/ | | 1777151973Sglebius V | | 1778151973Sglebius [active reader count] | | 177970700Sjulian | | 1780151973Sglebius Operation Pending -------------------------------+ | 178170700Sjulian | 1782151973Sglebius Active Writer ---------------------------------------+ 178371902Sjulian 178471902Sjulian 178570700Sjulian*/ 1786151973Sglebius#define WRITER_ACTIVE 0x00000001 1787151973Sglebius#define OP_PENDING 0x00000002 1788151973Sglebius#define READER_INCREMENT 0x00000004 1789151973Sglebius#define READER_MASK 0xfffffffc /* Not valid if WRITER_ACTIVE is set */ 1790151973Sglebius#define SAFETY_BARRIER 0x00100000 /* 128K items queued should be enough */ 179171902Sjulian 179271902Sjulian/* Defines of more elaborate states on the queue */ 1793151973Sglebius/* Mask of bits a new read cares about */ 1794151973Sglebius#define NGQ_RMASK (WRITER_ACTIVE|OP_PENDING) 179571902Sjulian 1796151973Sglebius/* Mask of bits a new write cares about */ 179771902Sjulian#define NGQ_WMASK (NGQ_RMASK|READER_MASK) 179871902Sjulian 1799151973Sglebius/* Test to decide if there is something on the queue. */ 1800151973Sglebius#define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING) 180171902Sjulian 1802151973Sglebius/* How to decide what the next queued item is. */ 1803151973Sglebius#define HEAD_IS_READER(QP) NGI_QUEUED_READER((QP)->queue) 1804151973Sglebius#define HEAD_IS_WRITER(QP) NGI_QUEUED_WRITER((QP)->queue) /* notused */ 1805151973Sglebius 1806151973Sglebius/* Read the status to decide if the next item on the queue can now run. */ 1807151973Sglebius#define QUEUED_READER_CAN_PROCEED(QP) \ 1808151973Sglebius (((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0) 1809151973Sglebius#define QUEUED_WRITER_CAN_PROCEED(QP) \ 1810151973Sglebius (((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0) 1811151973Sglebius 181271902Sjulian/* Is there a chance of getting ANY work off the queue? */ 1813151973Sglebius#define NEXT_QUEUED_ITEM_CAN_PROCEED(QP) \ 1814151973Sglebius (QUEUE_ACTIVE(QP) && \ 1815151973Sglebius ((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) : \ 1816151973Sglebius QUEUED_WRITER_CAN_PROCEED(QP))) 181771902Sjulian 1818151973Sglebius 1819151238Sglebius#define NGQRW_R 0 1820151238Sglebius#define NGQRW_W 1 1821151238Sglebius 182270700Sjulian/* 182370700Sjulian * Taking into account the current state of the queue and node, possibly take 182470700Sjulian * the next entry off the queue and return it. Return NULL if there was 182570700Sjulian * nothing we could return, either because there really was nothing there, or 182670700Sjulian * because the node was in a state where it cannot yet process the next item 182770700Sjulian * on the queue. 182870700Sjulian * 182970700Sjulian * This MUST MUST MUST be called with the mutex held. 183070700Sjulian */ 183170700Sjulianstatic __inline item_p 1832151238Sglebiusng_dequeue(struct ng_queue *ngq, int *rw) 183370700Sjulian{ 183470700Sjulian item_p item; 183570700Sjulian u_int add_arg; 183671902Sjulian 1837139039Sglebius mtx_assert(&ngq->q_mtx, MA_OWNED); 1838152451Sglebius /* 1839151973Sglebius * If there is nothing queued, then just return. 1840151973Sglebius * No point in continuing. 1841151973Sglebius * XXXGL: assert this? 1842151973Sglebius */ 1843151973Sglebius if (!QUEUE_ACTIVE(ngq)) { 1844154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; " 1845154275Sglebius "queue flags 0x%lx", __func__, 1846154275Sglebius ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags); 1847151973Sglebius return (NULL); 1848151973Sglebius } 1849139039Sglebius 1850151973Sglebius /* 1851151973Sglebius * From here, we can assume there is a head item. 1852151973Sglebius * We need to find out what it is and if it can be dequeued, given 1853151973Sglebius * the current state of the node. 1854151973Sglebius */ 1855151973Sglebius if (HEAD_IS_READER(ngq)) { 1856151973Sglebius if (!QUEUED_READER_CAN_PROCEED(ngq)) { 1857151973Sglebius /* 1858151973Sglebius * It's a reader but we can't use it. 1859151973Sglebius * We are stalled so make sure we don't 1860151973Sglebius * get called again until something changes. 1861151973Sglebius */ 1862151973Sglebius ng_worklist_remove(ngq->q_node); 1863154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) queued reader " 1864154275Sglebius "can't proceed; queue flags 0x%lx", __func__, 1865154275Sglebius ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags); 1866151973Sglebius return (NULL); 1867151973Sglebius } 186870700Sjulian /* 186971902Sjulian * Head of queue is a reader and we have no write active. 1870152451Sglebius * We don't care how many readers are already active. 1871151973Sglebius * Add the correct increment for the reader count. 187270700Sjulian */ 1873151973Sglebius add_arg = READER_INCREMENT; 1874151238Sglebius *rw = NGQRW_R; 1875151973Sglebius } else if (QUEUED_WRITER_CAN_PROCEED(ngq)) { 187670700Sjulian /* 187770700Sjulian * There is a pending write, no readers and no active writer. 187870700Sjulian * This means we can go ahead with the pending writer. Note 187970700Sjulian * the fact that we now have a writer, ready for when we take 188070700Sjulian * it off the queue. 188170700Sjulian * 188270700Sjulian * We don't need to worry about a possible collision with the 188370700Sjulian * fasttrack reader. 188470700Sjulian * 188570700Sjulian * The fasttrack thread may take a long time to discover that we 188670700Sjulian * are running so we would have an inconsistent state in the 188770700Sjulian * flags for a while. Since we ignore the reader count 188870700Sjulian * entirely when the WRITER_ACTIVE flag is set, this should 188970700Sjulian * not matter (in fact it is defined that way). If it tests 1890151973Sglebius * the flag before this operation, the OP_PENDING flag 189170700Sjulian * will make it fail, and if it tests it later, the 189271902Sjulian * WRITER_ACTIVE flag will do the same. If it is SO slow that 189370700Sjulian * we have actually completed the operation, and neither flag 1894151973Sglebius * is set by the time that it tests the flags, then it is 1895151973Sglebius * actually ok for it to continue. If it completes and we've 1896151973Sglebius * finished and the read pending is set it still fails. 189770700Sjulian * 189870700Sjulian * So we can just ignore it, as long as we can ensure that the 189970700Sjulian * transition from WRITE_PENDING state to the WRITER_ACTIVE 190070700Sjulian * state is atomic. 190170700Sjulian * 190270700Sjulian * After failing, first it will be held back by the mutex, then 190370700Sjulian * when it can proceed, it will queue its request, then it 190470700Sjulian * would arrive at this function. Usually it will have to 190571902Sjulian * leave empty handed because the ACTIVE WRITER bit will be 190670700Sjulian * set. 190771902Sjulian * 1908151973Sglebius * Adjust the flags for the new active writer. 190970700Sjulian */ 1910151973Sglebius add_arg = WRITER_ACTIVE; 1911151238Sglebius *rw = NGQRW_W; 191270700Sjulian /* 191370700Sjulian * We want to write "active writer, no readers " Now go make 191470700Sjulian * it true. In fact there may be a number in the readers 191570700Sjulian * count but we know it is not true and will be fixed soon. 191670700Sjulian * We will fix the flags for the next pending entry in a 191770700Sjulian * moment. 191870700Sjulian */ 191970700Sjulian } else { 192070700Sjulian /* 192170700Sjulian * We can't dequeue anything.. return and say so. Probably we 192270700Sjulian * have a write pending and the readers count is non zero. If 192370700Sjulian * we got here because a reader hit us just at the wrong 192470700Sjulian * moment with the fasttrack code, and put us in a strange 1925151973Sglebius * state, then it will be coming through in just a moment, 1926151973Sglebius * (just as soon as we release the mutex) and keep things 1927151973Sglebius * moving. 1928151973Sglebius * Make sure we remove ourselves from the work queue. It 1929151973Sglebius * would be a waste of effort to do all this again. 193070700Sjulian */ 193171902Sjulian ng_worklist_remove(ngq->q_node); 1932154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) can't dequeue anything; " 1933154275Sglebius "queue flags 0x%lx", __func__, 1934154275Sglebius ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags); 1935151973Sglebius return (NULL); 193670700Sjulian } 193752419Sjulian 193870700Sjulian /* 193970700Sjulian * Now we dequeue the request (whatever it may be) and correct the 194070700Sjulian * pending flags and the next and last pointers. 194170700Sjulian */ 194270700Sjulian item = ngq->queue; 194370700Sjulian ngq->queue = item->el_next; 1944154275Sglebius CTR6(KTR_NET, "%20s: node [%x] (%p) dequeued item %p with flags 0x%lx; " 1945154275Sglebius "queue flags 0x%lx", __func__, 1946154275Sglebius ngq->q_node->nd_ID,ngq->q_node, item, item->el_flags, ngq->q_flags); 194770700Sjulian if (ngq->last == &(item->el_next)) { 194870700Sjulian /* 194970700Sjulian * that was the last entry in the queue so set the 'last 1950151973Sglebius * pointer up correctly and make sure the pending flag is 195170700Sjulian * clear. 195270700Sjulian */ 1953151973Sglebius add_arg += -OP_PENDING; 195470700Sjulian ngq->last = &(ngq->queue); 195570700Sjulian /* 195671902Sjulian * Whatever flag was set will be cleared and 195771902Sjulian * the new acive field will be set by the add as well, 195871902Sjulian * so we don't need to change add_arg. 195971902Sjulian * But we know we don't need to be on the work list. 196070700Sjulian */ 196171902Sjulian atomic_add_long(&ngq->q_flags, add_arg); 196271902Sjulian ng_worklist_remove(ngq->q_node); 196370700Sjulian } else { 1964152451Sglebius /* 1965151973Sglebius * Since there is still something on the queue 1966151973Sglebius * we don't need to change the PENDING flag. 196771902Sjulian */ 196871902Sjulian atomic_add_long(&ngq->q_flags, add_arg); 196971902Sjulian /* 197071902Sjulian * If we see more doable work, make sure we are 197171902Sjulian * on the work queue. 197271902Sjulian */ 1973151973Sglebius if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) { 197471902Sjulian ng_setisr(ngq->q_node); 197571902Sjulian } 197670700Sjulian } 1977154275Sglebius CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; " 1978154275Sglebius "queue flags 0x%lx", __func__, 1979154275Sglebius ngq->q_node->nd_ID, ngq->q_node, item, *rw ? "WRITER" : "READER" , 1980154225Sglebius ngq->q_flags); 198170700Sjulian return (item); 198270700Sjulian} 198352419Sjulian 198470700Sjulian/* 198570700Sjulian * Queue a packet to be picked up by someone else. 198670700Sjulian * We really don't care who, but we can't or don't want to hang around 198770700Sjulian * to process it ourselves. We are probably an interrupt routine.. 1988151973Sglebius * If the queue could be run, flag the netisr handler to start. 198970700Sjulian */ 199070700Sjulianstatic __inline void 199170700Sjulianng_queue_rw(struct ng_queue * ngq, item_p item, int rw) 199270700Sjulian{ 1993139039Sglebius mtx_assert(&ngq->q_mtx, MA_OWNED); 1994139039Sglebius 1995151973Sglebius if (rw == NGQRW_W) 1996151973Sglebius NGI_SET_WRITER(item); 1997151973Sglebius else 1998151973Sglebius NGI_SET_READER(item); 199970700Sjulian item->el_next = NULL; /* maybe not needed */ 200070700Sjulian *ngq->last = item; 2001154275Sglebius CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__, 2002154275Sglebius ngq->q_node->nd_ID, ngq->q_node, item, rw ? "WRITER" : "READER" ); 200370700Sjulian /* 200470700Sjulian * If it was the first item in the queue then we need to 200570700Sjulian * set the last pointer and the type flags. 200670700Sjulian */ 2007154225Sglebius if (ngq->last == &(ngq->queue)) { 2008151973Sglebius atomic_add_long(&ngq->q_flags, OP_PENDING); 2009154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__, 2010154275Sglebius ngq->q_node->nd_ID, ngq->q_node); 2011154225Sglebius } 2012151973Sglebius 201370700Sjulian ngq->last = &(item->el_next); 2014151973Sglebius /* 2015151973Sglebius * We can take the worklist lock with the node locked 2016151973Sglebius * BUT NOT THE REVERSE! 2017151973Sglebius */ 2018151973Sglebius if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2019151973Sglebius ng_setisr(ngq->q_node); 202070700Sjulian} 202152419Sjulian 202270700Sjulian 202352419Sjulian/* 202470700Sjulian * This function 'cheats' in that it first tries to 'grab' the use of the 202570700Sjulian * node, without going through the mutex. We can do this becasue of the 202670700Sjulian * semantics of the lock. The semantics include a clause that says that the 202770700Sjulian * value of the readers count is invalid if the WRITER_ACTIVE flag is set. It 202870700Sjulian * also says that the WRITER_ACTIVE flag cannot be set if the readers count 202970700Sjulian * is not zero. Note that this talks about what is valid to SET the 203070700Sjulian * WRITER_ACTIVE flag, because from the moment it is set, the value if the 203170700Sjulian * reader count is immaterial, and not valid. The two 'pending' flags have a 203270700Sjulian * similar effect, in that If they are orthogonal to the two active fields in 203370700Sjulian * how they are set, but if either is set, the attempted 'grab' need to be 203470700Sjulian * backed out because there is earlier work, and we maintain ordering in the 203570700Sjulian * queue. The result of this is that the reader request can try obtain use of 203670700Sjulian * the node with only a single atomic addition, and without any of the mutex 203770700Sjulian * overhead. If this fails the operation degenerates to the same as for other 203870700Sjulian * cases. 203970700Sjulian * 204052419Sjulian */ 204170700Sjulianstatic __inline item_p 204270700Sjulianng_acquire_read(struct ng_queue *ngq, item_p item) 204352419Sjulian{ 2044151974Sglebius KASSERT(ngq != &ng_deadnode.nd_input_queue, 2045151974Sglebius ("%s: working on deadnode", __func__)); 204652419Sjulian 204770700Sjulian /* ######### Hack alert ######### */ 204870700Sjulian atomic_add_long(&ngq->q_flags, READER_INCREMENT); 204971902Sjulian if ((ngq->q_flags & NGQ_RMASK) == 0) { 205070700Sjulian /* Successfully grabbed node */ 2051154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) fast acquired item %p", 2052154275Sglebius __func__, ngq->q_node->nd_ID, ngq->q_node, item); 205370700Sjulian return (item); 205470700Sjulian } 205570700Sjulian /* undo the damage if we didn't succeed */ 205670700Sjulian atomic_subtract_long(&ngq->q_flags, READER_INCREMENT); 205770700Sjulian 205870700Sjulian /* ######### End Hack alert ######### */ 2059168049Swkoszek NG_QUEUE_LOCK(ngq); 206069922Sjulian /* 206170700Sjulian * Try again. Another processor (or interrupt for that matter) may 206270700Sjulian * have removed the last queued item that was stopping us from 206370700Sjulian * running, between the previous test, and the moment that we took 206470700Sjulian * the mutex. (Or maybe a writer completed.) 2065151973Sglebius * Even if another fast-track reader hits during this period 2066151973Sglebius * we don't care as multiple readers is OK. 206769922Sjulian */ 206871902Sjulian if ((ngq->q_flags & NGQ_RMASK) == 0) { 206970700Sjulian atomic_add_long(&ngq->q_flags, READER_INCREMENT); 2070168049Swkoszek NG_QUEUE_UNLOCK(ngq); 2071154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) slow acquired item %p", 2072154275Sglebius __func__, ngq->q_node->nd_ID, ngq->q_node, item); 207370700Sjulian return (item); 207470700Sjulian } 207570700Sjulian 207670700Sjulian /* 207770700Sjulian * and queue the request for later. 207870700Sjulian */ 207970700Sjulian ng_queue_rw(ngq, item, NGQRW_R); 2080168049Swkoszek NG_QUEUE_UNLOCK(ngq); 208170700Sjulian 2082148236Sglebius return (NULL); 208370700Sjulian} 208470700Sjulian 208570700Sjulianstatic __inline item_p 208670700Sjulianng_acquire_write(struct ng_queue *ngq, item_p item) 208770700Sjulian{ 2088151974Sglebius KASSERT(ngq != &ng_deadnode.nd_input_queue, 2089151974Sglebius ("%s: working on deadnode", __func__)); 2090151974Sglebius 209170700Sjulianrestart: 2092168049Swkoszek NG_QUEUE_LOCK(ngq); 209370700Sjulian /* 209470700Sjulian * If there are no readers, no writer, and no pending packets, then 209570700Sjulian * we can just go ahead. In all other situations we need to queue the 209670700Sjulian * request 209770700Sjulian */ 209871902Sjulian if ((ngq->q_flags & NGQ_WMASK) == 0) { 2099151973Sglebius /* collision could happen *HERE* */ 210070700Sjulian atomic_add_long(&ngq->q_flags, WRITER_ACTIVE); 2101168049Swkoszek NG_QUEUE_UNLOCK(ngq); 210270700Sjulian if (ngq->q_flags & READER_MASK) { 210370700Sjulian /* Collision with fast-track reader */ 210471902Sjulian atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE); 210570700Sjulian goto restart; 210669922Sjulian } 2107154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", 2108154275Sglebius __func__, ngq->q_node->nd_ID, ngq->q_node, item); 210970700Sjulian return (item); 211052419Sjulian } 211152419Sjulian 211270700Sjulian /* 211370700Sjulian * and queue the request for later. 211470700Sjulian */ 211570700Sjulian ng_queue_rw(ngq, item, NGQRW_W); 2116168049Swkoszek NG_QUEUE_UNLOCK(ngq); 211770700Sjulian 2118148236Sglebius return (NULL); 211970700Sjulian} 212070700Sjulian 2121167385Sjulian#if 0 2122167385Sjulianstatic __inline item_p 2123167385Sjulianng_upgrade_write(struct ng_queue *ngq, item_p item) 2124167385Sjulian{ 2125167385Sjulian KASSERT(ngq != &ng_deadnode.nd_input_queue, 2126167385Sjulian ("%s: working on deadnode", __func__)); 2127167385Sjulian 2128167385Sjulian NGI_SET_WRITER(item); 2129167385Sjulian 2130167385Sjulian mtx_lock_spin(&(ngq->q_mtx)); 2131167385Sjulian 2132167385Sjulian /* 2133167385Sjulian * There will never be no readers as we are there ourselves. 2134167385Sjulian * Set the WRITER_ACTIVE flags ASAP to block out fast track readers. 2135167385Sjulian * The caller we are running from will call ng_leave_read() 2136167385Sjulian * soon, so we must account for that. We must leave again with the 2137167385Sjulian * READER lock. If we find other readers, then 2138167385Sjulian * queue the request for later. However "later" may be rignt now 2139167385Sjulian * if there are no readers. We don't really care if there are queued 2140167385Sjulian * items as we will bypass them anyhow. 2141167385Sjulian */ 2142167385Sjulian atomic_add_long(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT); 2143167385Sjulian if (ngq->q_flags & (NGQ_WMASK & ~OP_PENDING) == WRITER_ACTIVE) { 2144167385Sjulian mtx_unlock_spin(&(ngq->q_mtx)); 2145167385Sjulian 2146167385Sjulian /* It's just us, act on the item. */ 2147167385Sjulian /* will NOT drop writer lock when done */ 2148167385Sjulian ng_apply_item(node, item, 0); 2149167385Sjulian 2150167385Sjulian /* 2151167385Sjulian * Having acted on the item, atomically 2152167385Sjulian * down grade back to READER and finish up 2153167385Sjulian */ 2154167385Sjulian atomic_add_long(&ngq->q_flags, 2155167385Sjulian READER_INCREMENT - WRITER_ACTIVE); 2156167385Sjulian 2157167385Sjulian /* Our caller will call ng_leave_read() */ 2158167385Sjulian return; 2159167385Sjulian } 2160167385Sjulian /* 2161167385Sjulian * It's not just us active, so queue us AT THE HEAD. 2162167385Sjulian * "Why?" I hear you ask. 2163167385Sjulian * Put us at the head of the queue as we've already been 2164167385Sjulian * through it once. If there is nothing else waiting, 2165167385Sjulian * set the correct flags. 2166167385Sjulian */ 2167167385Sjulian if ((item->el_next = ngq->queue) == NULL) { 2168167385Sjulian /* 2169167385Sjulian * Set up the "last" pointer. 2170167385Sjulian * We are the only (and thus last) item 2171167385Sjulian */ 2172167385Sjulian ngq->last = &(item->el_next); 2173167385Sjulian 2174167385Sjulian /* We've gone from, 0 to 1 item in the queue */ 2175167385Sjulian atomic_add_long(&ngq->q_flags, OP_PENDING); 2176167385Sjulian 2177167385Sjulian CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__, 2178167385Sjulian ngq->q_node->nd_ID, ngq->q_node); 2179167385Sjulian }; 2180167385Sjulian ngq->queue = item; 2181167385Sjulian CTR5(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER", 2182167385Sjulian __func__, ngq->q_node->nd_ID, ngq->q_node, item ); 2183167385Sjulian 2184167385Sjulian /* Reverse what we did above. That downgrades us back to reader */ 2185167385Sjulian atomic_add_long(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE); 2186167385Sjulian if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2187167385Sjulian ng_setisr(ngq->q_node); 2188167385Sjulian mtx_unlock_spin(&(ngq->q_mtx)); 2189167385Sjulian 2190167385Sjulian return; 2191167385Sjulian} 2192167385Sjulian 2193167385Sjulian#endif 2194167385Sjulian 219570700Sjulianstatic __inline void 219670700Sjulianng_leave_read(struct ng_queue *ngq) 219770700Sjulian{ 219870700Sjulian atomic_subtract_long(&ngq->q_flags, READER_INCREMENT); 219970700Sjulian} 220070700Sjulian 220170700Sjulianstatic __inline void 220270700Sjulianng_leave_write(struct ng_queue *ngq) 220370700Sjulian{ 220470700Sjulian atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE); 220570700Sjulian} 220670700Sjulian 220770700Sjulianstatic void 220870700Sjulianng_flush_input_queue(struct ng_queue * ngq) 220970700Sjulian{ 221070700Sjulian item_p item; 2211151973Sglebius 2212168049Swkoszek NG_QUEUE_LOCK(ngq); 2213151973Sglebius while (ngq->queue) { 221470700Sjulian item = ngq->queue; 221570700Sjulian ngq->queue = item->el_next; 221670700Sjulian if (ngq->last == &(item->el_next)) { 221770700Sjulian ngq->last = &(ngq->queue); 2218151973Sglebius atomic_add_long(&ngq->q_flags, -OP_PENDING); 221970700Sjulian } 2220168049Swkoszek NG_QUEUE_UNLOCK(ngq); 222170700Sjulian 2222151973Sglebius /* If the item is supplying a callback, call it with an error */ 2223151283Sglebius if (item->apply != NULL) { 2224151283Sglebius (item->apply)(item->context, ENOENT); 2225151283Sglebius item->apply = NULL; 2226151283Sglebius } 2227132369Sjulian NG_FREE_ITEM(item); 2228168049Swkoszek NG_QUEUE_LOCK(ngq); 222970700Sjulian } 223071902Sjulian /* 223171902Sjulian * Take us off the work queue if we are there. 2232167677Srwatson * We definately have no work to be done. 223371902Sjulian */ 223471902Sjulian ng_worklist_remove(ngq->q_node); 2235168049Swkoszek NG_QUEUE_UNLOCK(ngq); 223670700Sjulian} 223770700Sjulian 223870700Sjulian/*********************************************************************** 223970700Sjulian* Externally visible method for sending or queueing messages or data. 224070700Sjulian***********************************************************************/ 224170700Sjulian 224270700Sjulian/* 224371849Sjulian * The module code should have filled out the item correctly by this stage: 224470700Sjulian * Common: 224570700Sjulian * reference to destination node. 224670700Sjulian * Reference to destination rcv hook if relevant. 224770700Sjulian * Data: 224870700Sjulian * pointer to mbuf 224970700Sjulian * Control_Message: 225070700Sjulian * pointer to msg. 225170700Sjulian * ID of original sender node. (return address) 225271849Sjulian * Function: 225371849Sjulian * Function pointer 225471849Sjulian * void * argument 225571849Sjulian * integer argument 225670700Sjulian * 225770700Sjulian * The nodes have several routines and macros to help with this task: 225870700Sjulian */ 225970700Sjulian 226070700Sjulianint 2261146281Sglebiusng_snd_item(item_p item, int flags) 226270700Sjulian{ 226371849Sjulian hook_p hook = NGI_HOOK(item); 226471902Sjulian node_p node = NGI_NODE(item); 2265146281Sglebius int queue, rw; 226671902Sjulian struct ng_queue * ngq = &node->nd_input_queue; 2267151256Sglebius int error = 0; 226870700Sjulian 226970784Sjulian#ifdef NETGRAPH_DEBUG 2270152451Sglebius _ngi_check(item, __FILE__, __LINE__); 227170700Sjulian#endif 227270700Sjulian 2273146281Sglebius queue = (flags & NG_QUEUE) ? 1 : 0; 2274146281Sglebius 227570700Sjulian if (item == NULL) { 227671047Sjulian TRAP_ERROR(); 227770700Sjulian return (EINVAL); /* failed to get queue element */ 227870700Sjulian } 227971902Sjulian if (node == NULL) { 228070700Sjulian NG_FREE_ITEM(item); 228171047Sjulian TRAP_ERROR(); 228270700Sjulian return (EINVAL); /* No address */ 228370700Sjulian } 228471047Sjulian switch(item->el_flags & NGQF_TYPE) { 228571047Sjulian case NGQF_DATA: 228669922Sjulian /* 228770700Sjulian * DATA MESSAGE 228870700Sjulian * Delivered to a node via a non-optional hook. 228970700Sjulian * Both should be present in the item even though 229070700Sjulian * the node is derivable from the hook. 229170700Sjulian * References are held on both by the item. 229269922Sjulian */ 2293131112Sjulian 2294131112Sjulian /* Protect nodes from sending NULL pointers 2295131112Sjulian * to each other 2296131112Sjulian */ 2297131123Sjulian if (NGI_M(item) == NULL) 2298131112Sjulian return (EINVAL); 2299131112Sjulian 230070700Sjulian CHECK_DATA_MBUF(NGI_M(item)); 230170700Sjulian if (hook == NULL) { 230270700Sjulian NG_FREE_ITEM(item); 230371047Sjulian TRAP_ERROR(); 230470700Sjulian return(EINVAL); 230570700Sjulian } 230670784Sjulian if ((NG_HOOK_NOT_VALID(hook)) 230770784Sjulian || (NG_NODE_NOT_VALID(NG_HOOK_NODE(hook)))) { 230870700Sjulian NG_FREE_ITEM(item); 230970700Sjulian return (ENOTCONN); 231069922Sjulian } 231170784Sjulian if ((hook->hk_flags & HK_QUEUE)) { 231270700Sjulian queue = 1; 231370700Sjulian } 231471047Sjulian break; 231571047Sjulian case NGQF_MESG: 231670700Sjulian /* 231770700Sjulian * CONTROL MESSAGE 231870700Sjulian * Delivered to a node. 231970700Sjulian * Hook is optional. 232070700Sjulian * References are held by the item on the node and 232170700Sjulian * the hook if it is present. 232270700Sjulian */ 232370784Sjulian if (hook && (hook->hk_flags & HK_QUEUE)) { 232470700Sjulian queue = 1; 232570700Sjulian } 232671047Sjulian break; 232771047Sjulian case NGQF_FN: 232871047Sjulian break; 232971047Sjulian default: 233071047Sjulian NG_FREE_ITEM(item); 233171047Sjulian TRAP_ERROR(); 233271047Sjulian return (EINVAL); 233369922Sjulian } 2334149505Sglebius switch(item->el_flags & NGQF_RW) { 2335149505Sglebius case NGQF_READER: 2336149505Sglebius rw = NGQRW_R; 2337149505Sglebius break; 2338149505Sglebius case NGQF_WRITER: 2339149505Sglebius rw = NGQRW_W; 2340149505Sglebius break; 2341149505Sglebius default: 2342149505Sglebius panic("%s: invalid item flags %lx", __func__, item->el_flags); 2343149505Sglebius } 2344149505Sglebius 234570700Sjulian /* 2346149505Sglebius * If the node specifies single threading, force writer semantics. 2347149505Sglebius * Similarly, the node may say one hook always produces writers. 2348151238Sglebius * These are overrides. 234970700Sjulian */ 2350132464Sjulian if ((node->nd_flags & NGF_FORCE_WRITER) 2351151238Sglebius || (hook && (hook->hk_flags & HK_FORCE_WRITER))) 235270700Sjulian rw = NGQRW_W; 2353149505Sglebius 235470700Sjulian if (queue) { 235570700Sjulian /* Put it on the queue for that node*/ 235670784Sjulian#ifdef NETGRAPH_DEBUG 2357152451Sglebius _ngi_check(item, __FILE__, __LINE__); 235870700Sjulian#endif 2359168049Swkoszek NG_QUEUE_LOCK(ngq); 236070700Sjulian ng_queue_rw(ngq, item, rw); 2361168049Swkoszek NG_QUEUE_UNLOCK(ngq); 2362147774Sglebius 2363147774Sglebius if (flags & NG_PROGRESS) 2364147774Sglebius return (EINPROGRESS); 2365147774Sglebius else 2366147774Sglebius return (0); 236770700Sjulian } 236869922Sjulian 236970700Sjulian /* 237070700Sjulian * We already decided how we will be queueud or treated. 237170700Sjulian * Try get the appropriate operating permission. 237270700Sjulian */ 2373151256Sglebius if (rw == NGQRW_R) 237470700Sjulian item = ng_acquire_read(ngq, item); 2375151256Sglebius else 237670700Sjulian item = ng_acquire_write(ngq, item); 237752419Sjulian 2378151973Sglebius 237970700Sjulian if (item == NULL) { 2380147774Sglebius if (flags & NG_PROGRESS) 2381147774Sglebius return (EINPROGRESS); 2382147774Sglebius else 2383147774Sglebius return (0); 238470700Sjulian } 238552419Sjulian 238670784Sjulian#ifdef NETGRAPH_DEBUG 2387152451Sglebius _ngi_check(item, __FILE__, __LINE__); 238870700Sjulian#endif 2389151973Sglebius 239074078Sjulian NGI_GET_NODE(item, node); /* zaps stored node */ 239152419Sjulian 2392170180Sglebius error = ng_apply_item(node, item, rw); /* drops r/w lock when done */ 239374078Sjulian 239452419Sjulian /* 2395152451Sglebius * If the node goes away when we remove the reference, 239682058Sbrian * whatever we just did caused it.. whatever we do, DO NOT 239774078Sjulian * access the node again! 239874078Sjulian */ 239974078Sjulian if (NG_NODE_UNREF(node) == 0) { 240074078Sjulian return (error); 240174078Sjulian } 240274078Sjulian 2403168049Swkoszek NG_QUEUE_LOCK(ngq); 2404151973Sglebius if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2405148236Sglebius ng_setisr(ngq->q_node); 2406168049Swkoszek NG_QUEUE_UNLOCK(ngq); 240770700Sjulian 240871849Sjulian return (error); 240952419Sjulian} 241052419Sjulian 241152419Sjulian/* 241270700Sjulian * We have an item that was possibly queued somewhere. 241370700Sjulian * It should contain all the information needed 241470700Sjulian * to run it on the appropriate node/hook. 241552419Sjulian */ 2416170180Sglebiusstatic int 2417151238Sglebiusng_apply_item(node_p node, item_p item, int rw) 241852419Sjulian{ 241970700Sjulian hook_p hook; 2420167402Sjulian int error = 0; 242170700Sjulian ng_rcvdata_t *rcvdata; 242271885Sjulian ng_rcvmsg_t *rcvmsg; 2423147774Sglebius ng_apply_t *apply = NULL; 2424147774Sglebius void *context = NULL; 242552419Sjulian 242671849Sjulian NGI_GET_HOOK(item, hook); /* clears stored hook */ 242770784Sjulian#ifdef NETGRAPH_DEBUG 2428152451Sglebius _ngi_check(item, __FILE__, __LINE__); 242970700Sjulian#endif 2430147774Sglebius 2431147774Sglebius /* 2432151973Sglebius * If the item has an "apply" callback, store it. 2433151973Sglebius * Clear item's callback immediately, to avoid an extra call if 2434151973Sglebius * the item is reused by the destination node. 2435147774Sglebius */ 2436147774Sglebius if (item->apply != NULL) { 2437147774Sglebius apply = item->apply; 2438147774Sglebius context = item->context; 2439147774Sglebius item->apply = NULL; 2440147774Sglebius } 2441147774Sglebius 244271047Sjulian switch (item->el_flags & NGQF_TYPE) { 244370700Sjulian case NGQF_DATA: 244470700Sjulian /* 244570700Sjulian * Check things are still ok as when we were queued. 244670700Sjulian */ 244770700Sjulian if ((hook == NULL) 244870784Sjulian || NG_HOOK_NOT_VALID(hook) 244971885Sjulian || NG_NODE_NOT_VALID(node) ) { 2450167402Sjulian error = EIO; 245170700Sjulian NG_FREE_ITEM(item); 245271885Sjulian break; 245370700Sjulian } 245471885Sjulian /* 245571885Sjulian * If no receive method, just silently drop it. 245671885Sjulian * Give preference to the hook over-ride method 245771885Sjulian */ 2458152451Sglebius if ((!(rcvdata = hook->hk_rcvdata)) 245971885Sjulian && (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) { 246071885Sjulian error = 0; 246171885Sjulian NG_FREE_ITEM(item); 246271885Sjulian break; 246371885Sjulian } 2464167402Sjulian error = (*rcvdata)(hook, item); 246570700Sjulian break; 246670700Sjulian case NGQF_MESG: 246770700Sjulian if (hook) { 246870784Sjulian if (NG_HOOK_NOT_VALID(hook)) { 246971849Sjulian /* 247071849Sjulian * The hook has been zapped then we can't 2471167677Srwatson * use it. Immediately drop its reference. 247271849Sjulian * The message may not need it. 247371849Sjulian */ 247470784Sjulian NG_HOOK_UNREF(hook); 247570700Sjulian hook = NULL; 247670700Sjulian } 247770700Sjulian } 247870700Sjulian /* 247970700Sjulian * Similarly, if the node is a zombie there is 248070700Sjulian * nothing we can do with it, drop everything. 248170700Sjulian */ 248270784Sjulian if (NG_NODE_NOT_VALID(node)) { 248371047Sjulian TRAP_ERROR(); 2484167402Sjulian error = EINVAL; 248570700Sjulian NG_FREE_ITEM(item); 248670700Sjulian } else { 248770700Sjulian /* 248870700Sjulian * Call the appropriate message handler for the object. 248970700Sjulian * It is up to the message handler to free the message. 249070700Sjulian * If it's a generic message, handle it generically, 249170700Sjulian * otherwise call the type's message handler 249270700Sjulian * (if it exists) 249370700Sjulian * XXX (race). Remember that a queued message may 249470700Sjulian * reference a node or hook that has just been 249570700Sjulian * invalidated. It will exist as the queue code 249670700Sjulian * is holding a reference, but.. 249770700Sjulian */ 249870700Sjulian 249970700Sjulian struct ng_mesg *msg = NGI_MSG(item); 250070700Sjulian 2501152451Sglebius /* 250271885Sjulian * check if the generic handler owns it. 250371885Sjulian */ 250470700Sjulian if ((msg->header.typecookie == NGM_GENERIC_COOKIE) 250570700Sjulian && ((msg->header.flags & NGF_RESP) == 0)) { 2506167402Sjulian error = ng_generic_msg(node, item, hook); 250771885Sjulian break; 250870700Sjulian } 250971885Sjulian /* 251071885Sjulian * Now see if there is a handler (hook or node specific) 251171885Sjulian * in the target node. If none, silently discard. 251271885Sjulian */ 251371885Sjulian if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) 251471885Sjulian && (!(rcvmsg = node->nd_type->rcvmsg))) { 251571885Sjulian TRAP_ERROR(); 2516167402Sjulian error = 0; 251771885Sjulian NG_FREE_ITEM(item); 251871885Sjulian break; 251971885Sjulian } 2520167402Sjulian error = (*rcvmsg)(node, item, hook); 252170700Sjulian } 252270700Sjulian break; 252371047Sjulian case NGQF_FN: 252471047Sjulian /* 252571047Sjulian * We have to implicitly trust the hook, 252671047Sjulian * as some of these are used for system purposes 252771849Sjulian * where the hook is invalid. In the case of 252871849Sjulian * the shutdown message we allow it to hit 252971849Sjulian * even if the node is invalid. 253071047Sjulian */ 2531152451Sglebius if ((NG_NODE_NOT_VALID(node)) 253271849Sjulian && (NGI_FN(item) != &ng_rmnode)) { 253371047Sjulian TRAP_ERROR(); 2534167402Sjulian error = EINVAL; 2535143384Sglebius NG_FREE_ITEM(item); 253671047Sjulian break; 253771047Sjulian } 253871849Sjulian (*NGI_FN(item))(node, hook, NGI_ARG1(item), NGI_ARG2(item)); 253971047Sjulian NG_FREE_ITEM(item); 254071047Sjulian break; 254171047Sjulian 254270700Sjulian } 254370700Sjulian /* 254470700Sjulian * We held references on some of the resources 254570700Sjulian * that we took from the item. Now that we have 254670700Sjulian * finished doing everything, drop those references. 254770700Sjulian */ 254870700Sjulian if (hook) { 254970784Sjulian NG_HOOK_UNREF(hook); 255070700Sjulian } 255170700Sjulian 2552151238Sglebius if (rw == NGQRW_R) { 255370784Sjulian ng_leave_read(&node->nd_input_queue); 2554167402Sjulian } else { 255570784Sjulian ng_leave_write(&node->nd_input_queue); 2556167402Sjulian } 2557147774Sglebius 2558147774Sglebius /* Apply callback. */ 2559147774Sglebius if (apply != NULL) 2560147774Sglebius (*apply)(context, error); 2561147774Sglebius 2562170180Sglebius return (error); 256370700Sjulian} 256470700Sjulian 256570700Sjulian/*********************************************************************** 256670700Sjulian * Implement the 'generic' control messages 256770700Sjulian ***********************************************************************/ 256870700Sjulianstatic int 256970700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook) 257070700Sjulian{ 257170700Sjulian int error = 0; 257270700Sjulian struct ng_mesg *msg; 257370700Sjulian struct ng_mesg *resp = NULL; 257470700Sjulian 257570700Sjulian NGI_GET_MSG(item, msg); 257652419Sjulian if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 257771047Sjulian TRAP_ERROR(); 257870700Sjulian error = EINVAL; 257970700Sjulian goto out; 258052419Sjulian } 258152419Sjulian switch (msg->header.cmd) { 258252419Sjulian case NGM_SHUTDOWN: 258371849Sjulian ng_rmnode(here, NULL, NULL, 0); 258452419Sjulian break; 258552419Sjulian case NGM_MKPEER: 258652419Sjulian { 258752419Sjulian struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 258852419Sjulian 258952419Sjulian if (msg->header.arglen != sizeof(*mkp)) { 259071047Sjulian TRAP_ERROR(); 259170700Sjulian error = EINVAL; 259270700Sjulian break; 259352419Sjulian } 259452419Sjulian mkp->type[sizeof(mkp->type) - 1] = '\0'; 259552419Sjulian mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 259652419Sjulian mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 259752419Sjulian error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 259852419Sjulian break; 259952419Sjulian } 260052419Sjulian case NGM_CONNECT: 260152419Sjulian { 260252419Sjulian struct ngm_connect *const con = 260352419Sjulian (struct ngm_connect *) msg->data; 260452419Sjulian node_p node2; 260552419Sjulian 260652419Sjulian if (msg->header.arglen != sizeof(*con)) { 260771047Sjulian TRAP_ERROR(); 260870700Sjulian error = EINVAL; 260970700Sjulian break; 261052419Sjulian } 261152419Sjulian con->path[sizeof(con->path) - 1] = '\0'; 261252419Sjulian con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 261352419Sjulian con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 261470700Sjulian /* Don't forget we get a reference.. */ 261570700Sjulian error = ng_path2noderef(here, con->path, &node2, NULL); 261652419Sjulian if (error) 261752419Sjulian break; 261852419Sjulian error = ng_con_nodes(here, con->ourhook, node2, con->peerhook); 261970784Sjulian NG_NODE_UNREF(node2); 262052419Sjulian break; 262152419Sjulian } 262252419Sjulian case NGM_NAME: 262352419Sjulian { 262452419Sjulian struct ngm_name *const nam = (struct ngm_name *) msg->data; 262552419Sjulian 262652419Sjulian if (msg->header.arglen != sizeof(*nam)) { 262771047Sjulian TRAP_ERROR(); 262870700Sjulian error = EINVAL; 262970700Sjulian break; 263052419Sjulian } 263152419Sjulian nam->name[sizeof(nam->name) - 1] = '\0'; 263252419Sjulian error = ng_name_node(here, nam->name); 263352419Sjulian break; 263452419Sjulian } 263552419Sjulian case NGM_RMHOOK: 263652419Sjulian { 263752419Sjulian struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 263852419Sjulian hook_p hook; 263952419Sjulian 264052419Sjulian if (msg->header.arglen != sizeof(*rmh)) { 264171047Sjulian TRAP_ERROR(); 264270700Sjulian error = EINVAL; 264370700Sjulian break; 264452419Sjulian } 264552419Sjulian rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 264654096Sarchie if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 264752419Sjulian ng_destroy_hook(hook); 264852419Sjulian break; 264952419Sjulian } 265052419Sjulian case NGM_NODEINFO: 265152419Sjulian { 265252419Sjulian struct nodeinfo *ni; 265352419Sjulian 265470700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT); 265552419Sjulian if (resp == NULL) { 265652419Sjulian error = ENOMEM; 265752419Sjulian break; 265852419Sjulian } 265952419Sjulian 266052419Sjulian /* Fill in node info */ 266170700Sjulian ni = (struct nodeinfo *) resp->data; 266270784Sjulian if (NG_NODE_HAS_NAME(here)) 2663125028Sharti strcpy(ni->name, NG_NODE_NAME(here)); 2664125028Sharti strcpy(ni->type, here->nd_type->name); 266552722Sjulian ni->id = ng_node2ID(here); 266670784Sjulian ni->hooks = here->nd_numhooks; 266752419Sjulian break; 266852419Sjulian } 266952419Sjulian case NGM_LISTHOOKS: 267052419Sjulian { 267170784Sjulian const int nhooks = here->nd_numhooks; 267252419Sjulian struct hooklist *hl; 267352419Sjulian struct nodeinfo *ni; 267452419Sjulian hook_p hook; 267552419Sjulian 267652419Sjulian /* Get response struct */ 267770700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*hl) 267870700Sjulian + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 267952419Sjulian if (resp == NULL) { 268052419Sjulian error = ENOMEM; 268152419Sjulian break; 268252419Sjulian } 268370700Sjulian hl = (struct hooklist *) resp->data; 268452419Sjulian ni = &hl->nodeinfo; 268552419Sjulian 268652419Sjulian /* Fill in node info */ 268770784Sjulian if (NG_NODE_HAS_NAME(here)) 2688125028Sharti strcpy(ni->name, NG_NODE_NAME(here)); 2689125028Sharti strcpy(ni->type, here->nd_type->name); 269052722Sjulian ni->id = ng_node2ID(here); 269152419Sjulian 269252419Sjulian /* Cycle through the linked list of hooks */ 269352419Sjulian ni->hooks = 0; 269470784Sjulian LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) { 269552419Sjulian struct linkinfo *const link = &hl->link[ni->hooks]; 269652419Sjulian 269752419Sjulian if (ni->hooks >= nhooks) { 269852419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 269987599Sobrien __func__, "hooks"); 270052419Sjulian break; 270152419Sjulian } 270270784Sjulian if (NG_HOOK_NOT_VALID(hook)) 270352419Sjulian continue; 2704125028Sharti strcpy(link->ourhook, NG_HOOK_NAME(hook)); 2705125028Sharti strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook)); 270670784Sjulian if (NG_PEER_NODE_NAME(hook)[0] != '\0') 2707125028Sharti strcpy(link->nodeinfo.name, 2708125028Sharti NG_PEER_NODE_NAME(hook)); 2709125028Sharti strcpy(link->nodeinfo.type, 2710125028Sharti NG_PEER_NODE(hook)->nd_type->name); 271170784Sjulian link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook)); 271270784Sjulian link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks; 271352419Sjulian ni->hooks++; 271452419Sjulian } 271552419Sjulian break; 271652419Sjulian } 271752419Sjulian 271852419Sjulian case NGM_LISTNAMES: 271952419Sjulian case NGM_LISTNODES: 272052419Sjulian { 272152419Sjulian const int unnamed = (msg->header.cmd == NGM_LISTNODES); 272252419Sjulian struct namelist *nl; 272352419Sjulian node_p node; 272452419Sjulian int num = 0; 272552419Sjulian 272672200Sbmilekic mtx_lock(&ng_nodelist_mtx); 272752419Sjulian /* Count number of nodes */ 272870784Sjulian LIST_FOREACH(node, &ng_nodelist, nd_nodes) { 272970912Sjulian if (NG_NODE_IS_VALID(node) 273070912Sjulian && (unnamed || NG_NODE_HAS_NAME(node))) { 273152419Sjulian num++; 273270912Sjulian } 273352419Sjulian } 273472200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 273552419Sjulian 273652419Sjulian /* Get response struct */ 273770700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*nl) 273870700Sjulian + (num * sizeof(struct nodeinfo)), M_NOWAIT); 273952419Sjulian if (resp == NULL) { 274052419Sjulian error = ENOMEM; 274152419Sjulian break; 274252419Sjulian } 274370700Sjulian nl = (struct namelist *) resp->data; 274452419Sjulian 274552419Sjulian /* Cycle through the linked list of nodes */ 274652419Sjulian nl->numnames = 0; 274772200Sbmilekic mtx_lock(&ng_nodelist_mtx); 274870784Sjulian LIST_FOREACH(node, &ng_nodelist, nd_nodes) { 274952419Sjulian struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; 275052419Sjulian 2751159373Sglebius if (NG_NODE_NOT_VALID(node)) 2752159373Sglebius continue; 2753159373Sglebius if (!unnamed && (! NG_NODE_HAS_NAME(node))) 2754159373Sglebius continue; 275552419Sjulian if (nl->numnames >= num) { 275652419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 275787599Sobrien __func__, "nodes"); 275852419Sjulian break; 275952419Sjulian } 276070784Sjulian if (NG_NODE_HAS_NAME(node)) 2761125028Sharti strcpy(np->name, NG_NODE_NAME(node)); 2762125028Sharti strcpy(np->type, node->nd_type->name); 276352722Sjulian np->id = ng_node2ID(node); 276470784Sjulian np->hooks = node->nd_numhooks; 276552419Sjulian nl->numnames++; 276652419Sjulian } 276772200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 276852419Sjulian break; 276952419Sjulian } 277052419Sjulian 277152419Sjulian case NGM_LISTTYPES: 277252419Sjulian { 277352419Sjulian struct typelist *tl; 277452419Sjulian struct ng_type *type; 277552419Sjulian int num = 0; 277652419Sjulian 277772200Sbmilekic mtx_lock(&ng_typelist_mtx); 277852419Sjulian /* Count number of types */ 277970912Sjulian LIST_FOREACH(type, &ng_typelist, types) { 278052419Sjulian num++; 278170912Sjulian } 278272200Sbmilekic mtx_unlock(&ng_typelist_mtx); 278352419Sjulian 278452419Sjulian /* Get response struct */ 278570700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*tl) 278670700Sjulian + (num * sizeof(struct typeinfo)), M_NOWAIT); 278752419Sjulian if (resp == NULL) { 278852419Sjulian error = ENOMEM; 278952419Sjulian break; 279052419Sjulian } 279170700Sjulian tl = (struct typelist *) resp->data; 279252419Sjulian 279352419Sjulian /* Cycle through the linked list of types */ 279452419Sjulian tl->numtypes = 0; 279572200Sbmilekic mtx_lock(&ng_typelist_mtx); 279670700Sjulian LIST_FOREACH(type, &ng_typelist, types) { 279752419Sjulian struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 279852419Sjulian 279952419Sjulian if (tl->numtypes >= num) { 280052419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 280187599Sobrien __func__, "types"); 280252419Sjulian break; 280352419Sjulian } 2804125028Sharti strcpy(tp->type_name, type->name); 280571603Sjulian tp->numnodes = type->refs - 1; /* don't count list */ 280652419Sjulian tl->numtypes++; 280752419Sjulian } 280872200Sbmilekic mtx_unlock(&ng_typelist_mtx); 280952419Sjulian break; 281052419Sjulian } 281152419Sjulian 281253913Sarchie case NGM_BINARY2ASCII: 281353913Sarchie { 281464510Sarchie int bufSize = 20 * 1024; /* XXX hard coded constant */ 281553913Sarchie const struct ng_parse_type *argstype; 281653913Sarchie const struct ng_cmdlist *c; 281770700Sjulian struct ng_mesg *binary, *ascii; 281853913Sarchie 281953913Sarchie /* Data area must contain a valid netgraph message */ 282053913Sarchie binary = (struct ng_mesg *)msg->data; 2821152451Sglebius if (msg->header.arglen < sizeof(struct ng_mesg) || 2822152451Sglebius (msg->header.arglen - sizeof(struct ng_mesg) < 2823152451Sglebius binary->header.arglen)) { 282471047Sjulian TRAP_ERROR(); 282553913Sarchie error = EINVAL; 282653913Sarchie break; 282753913Sarchie } 282853913Sarchie 282970700Sjulian /* Get a response message with lots of room */ 283070700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 283170159Sjulian if (resp == NULL) { 283253913Sarchie error = ENOMEM; 283353913Sarchie break; 283453913Sarchie } 283570700Sjulian ascii = (struct ng_mesg *)resp->data; 283653913Sarchie 283753913Sarchie /* Copy binary message header to response message payload */ 283853913Sarchie bcopy(binary, ascii, sizeof(*binary)); 283953913Sarchie 284053913Sarchie /* Find command by matching typecookie and command number */ 284170784Sjulian for (c = here->nd_type->cmdlist; 284253913Sarchie c != NULL && c->name != NULL; c++) { 284353913Sarchie if (binary->header.typecookie == c->cookie 284453913Sarchie && binary->header.cmd == c->cmd) 284553913Sarchie break; 284653913Sarchie } 284753913Sarchie if (c == NULL || c->name == NULL) { 284853913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 284953913Sarchie if (binary->header.typecookie == c->cookie 285053913Sarchie && binary->header.cmd == c->cmd) 285153913Sarchie break; 285253913Sarchie } 285353913Sarchie if (c->name == NULL) { 285470700Sjulian NG_FREE_MSG(resp); 285553913Sarchie error = ENOSYS; 285653913Sarchie break; 285753913Sarchie } 285853913Sarchie } 285953913Sarchie 286053913Sarchie /* Convert command name to ASCII */ 286153913Sarchie snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 286253913Sarchie "%s", c->name); 286353913Sarchie 286453913Sarchie /* Convert command arguments to ASCII */ 286553913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 286653913Sarchie c->respType : c->mesgType; 286770912Sjulian if (argstype == NULL) { 286853913Sarchie *ascii->data = '\0'; 286970912Sjulian } else { 287053913Sarchie if ((error = ng_unparse(argstype, 287153913Sarchie (u_char *)binary->data, 287253913Sarchie ascii->data, bufSize)) != 0) { 287370700Sjulian NG_FREE_MSG(resp); 287453913Sarchie break; 287553913Sarchie } 287653913Sarchie } 287753913Sarchie 287853913Sarchie /* Return the result as struct ng_mesg plus ASCII string */ 287953913Sarchie bufSize = strlen(ascii->data) + 1; 288053913Sarchie ascii->header.arglen = bufSize; 288170700Sjulian resp->header.arglen = sizeof(*ascii) + bufSize; 288253913Sarchie break; 288353913Sarchie } 288453913Sarchie 288553913Sarchie case NGM_ASCII2BINARY: 288653913Sarchie { 288753913Sarchie int bufSize = 2000; /* XXX hard coded constant */ 288853913Sarchie const struct ng_cmdlist *c; 288953913Sarchie const struct ng_parse_type *argstype; 289070700Sjulian struct ng_mesg *ascii, *binary; 289159178Sarchie int off = 0; 289253913Sarchie 289353913Sarchie /* Data area must contain at least a struct ng_mesg + '\0' */ 289453913Sarchie ascii = (struct ng_mesg *)msg->data; 2895152451Sglebius if ((msg->header.arglen < sizeof(*ascii) + 1) || 2896152451Sglebius (ascii->header.arglen < 1) || 2897152451Sglebius (msg->header.arglen < sizeof(*ascii) + 2898152451Sglebius ascii->header.arglen)) { 289971047Sjulian TRAP_ERROR(); 290053913Sarchie error = EINVAL; 290153913Sarchie break; 290253913Sarchie } 290353913Sarchie ascii->data[ascii->header.arglen - 1] = '\0'; 290453913Sarchie 290570700Sjulian /* Get a response message with lots of room */ 290670700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 290770159Sjulian if (resp == NULL) { 290853913Sarchie error = ENOMEM; 290953913Sarchie break; 291053913Sarchie } 291170700Sjulian binary = (struct ng_mesg *)resp->data; 291253913Sarchie 291353913Sarchie /* Copy ASCII message header to response message payload */ 291453913Sarchie bcopy(ascii, binary, sizeof(*ascii)); 291553913Sarchie 291653913Sarchie /* Find command by matching ASCII command string */ 291770784Sjulian for (c = here->nd_type->cmdlist; 291853913Sarchie c != NULL && c->name != NULL; c++) { 291953913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 292053913Sarchie break; 292153913Sarchie } 292253913Sarchie if (c == NULL || c->name == NULL) { 292353913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 292453913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 292553913Sarchie break; 292653913Sarchie } 292753913Sarchie if (c->name == NULL) { 292870700Sjulian NG_FREE_MSG(resp); 292953913Sarchie error = ENOSYS; 293053913Sarchie break; 293153913Sarchie } 293253913Sarchie } 293353913Sarchie 293453913Sarchie /* Convert command name to binary */ 293553913Sarchie binary->header.cmd = c->cmd; 293653913Sarchie binary->header.typecookie = c->cookie; 293753913Sarchie 293853913Sarchie /* Convert command arguments to binary */ 293953913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 294053913Sarchie c->respType : c->mesgType; 294170912Sjulian if (argstype == NULL) { 294253913Sarchie bufSize = 0; 294370912Sjulian } else { 294453913Sarchie if ((error = ng_parse(argstype, ascii->data, 294553913Sarchie &off, (u_char *)binary->data, &bufSize)) != 0) { 294670700Sjulian NG_FREE_MSG(resp); 294753913Sarchie break; 294853913Sarchie } 294953913Sarchie } 295053913Sarchie 295153913Sarchie /* Return the result */ 295253913Sarchie binary->header.arglen = bufSize; 295370700Sjulian resp->header.arglen = sizeof(*binary) + bufSize; 295453913Sarchie break; 295553913Sarchie } 295653913Sarchie 295762471Sphk case NGM_TEXT_CONFIG: 295852419Sjulian case NGM_TEXT_STATUS: 295952419Sjulian /* 296052419Sjulian * This one is tricky as it passes the command down to the 296152419Sjulian * actual node, even though it is a generic type command. 296270700Sjulian * This means we must assume that the item/msg is already freed 296352419Sjulian * when control passes back to us. 296452419Sjulian */ 296570784Sjulian if (here->nd_type->rcvmsg != NULL) { 296670700Sjulian NGI_MSG(item) = msg; /* put it back as we found it */ 296770784Sjulian return((*here->nd_type->rcvmsg)(here, item, lasthook)); 296852419Sjulian } 296952419Sjulian /* Fall through if rcvmsg not supported */ 297052419Sjulian default: 297171047Sjulian TRAP_ERROR(); 297252419Sjulian error = EINVAL; 297352419Sjulian } 297470700Sjulian /* 297570700Sjulian * Sometimes a generic message may be statically allocated 297670700Sjulian * to avoid problems with allocating when in tight memeory situations. 297770700Sjulian * Don't free it if it is so. 297870700Sjulian * I break them appart here, because erros may cause a free if the item 297970700Sjulian * in which case we'd be doing it twice. 298070700Sjulian * they are kept together above, to simplify freeing. 298170700Sjulian */ 298270700Sjulianout: 298370700Sjulian NG_RESPOND_MSG(error, here, item, resp); 298471849Sjulian if (msg) 298570700Sjulian NG_FREE_MSG(msg); 298652419Sjulian return (error); 298752419Sjulian} 298852419Sjulian 298952419Sjulian/************************************************************************ 2990146213Sglebius Queue element get/free routines 2991146213Sglebius************************************************************************/ 2992146213Sglebius 2993146213Sglebiusuma_zone_t ng_qzone; 2994146213Sglebiusstatic int maxalloc = 512; /* limit the damage of a leak */ 2995146213Sglebius 2996146213SglebiusTUNABLE_INT("net.graph.maxalloc", &maxalloc); 2997146213SglebiusSYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc, 2998146213Sglebius 0, "Maximum number of queue items to allocate"); 2999146213Sglebius 3000146213Sglebius#ifdef NETGRAPH_DEBUG 3001146213Sglebiusstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist); 3002146213Sglebiusstatic int allocated; /* number of items malloc'd */ 3003146213Sglebius#endif 3004146213Sglebius 3005146213Sglebius/* 3006146213Sglebius * Get a queue entry. 3007146213Sglebius * This is usually called when a packet first enters netgraph. 3008146213Sglebius * By definition, this is usually from an interrupt, or from a user. 3009146213Sglebius * Users are not so important, but try be quick for the times that it's 3010146213Sglebius * an interrupt. 3011146213Sglebius */ 3012146213Sglebiusstatic __inline item_p 3013146281Sglebiusng_getqblk(int flags) 3014146213Sglebius{ 3015146213Sglebius item_p item = NULL; 3016146281Sglebius int wait; 3017146213Sglebius 3018146281Sglebius wait = (flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT; 3019146213Sglebius 3020146281Sglebius item = uma_zalloc(ng_qzone, wait | M_ZERO); 3021146281Sglebius 3022146213Sglebius#ifdef NETGRAPH_DEBUG 3023146213Sglebius if (item) { 3024146213Sglebius mtx_lock(&ngq_mtx); 3025146213Sglebius TAILQ_INSERT_TAIL(&ng_itemlist, item, all); 3026146213Sglebius allocated++; 3027146213Sglebius mtx_unlock(&ngq_mtx); 3028146213Sglebius } 3029146213Sglebius#endif 3030146213Sglebius 3031146213Sglebius return (item); 3032146213Sglebius} 3033146213Sglebius 3034146213Sglebius/* 3035146213Sglebius * Release a queue entry 3036146213Sglebius */ 3037146213Sglebiusvoid 3038146213Sglebiusng_free_item(item_p item) 3039146213Sglebius{ 3040151283Sglebius KASSERT(item->apply == NULL, ("%s: leaking apply callback", __func__)); 3041151283Sglebius 3042146213Sglebius /* 3043146213Sglebius * The item may hold resources on it's own. We need to free 3044146213Sglebius * these before we can free the item. What they are depends upon 3045146213Sglebius * what kind of item it is. it is important that nodes zero 3046146213Sglebius * out pointers to resources that they remove from the item 3047146213Sglebius * or we release them again here. 3048146213Sglebius */ 3049146213Sglebius switch (item->el_flags & NGQF_TYPE) { 3050146213Sglebius case NGQF_DATA: 3051146213Sglebius /* If we have an mbuf still attached.. */ 3052146213Sglebius NG_FREE_M(_NGI_M(item)); 3053146213Sglebius break; 3054146213Sglebius case NGQF_MESG: 3055146213Sglebius _NGI_RETADDR(item) = 0; 3056146213Sglebius NG_FREE_MSG(_NGI_MSG(item)); 3057146213Sglebius break; 3058146213Sglebius case NGQF_FN: 3059146213Sglebius /* nothing to free really, */ 3060146213Sglebius _NGI_FN(item) = NULL; 3061146213Sglebius _NGI_ARG1(item) = NULL; 3062146213Sglebius _NGI_ARG2(item) = 0; 3063146213Sglebius case NGQF_UNDEF: 3064146213Sglebius break; 3065146213Sglebius } 3066146213Sglebius /* If we still have a node or hook referenced... */ 3067146213Sglebius _NGI_CLR_NODE(item); 3068146213Sglebius _NGI_CLR_HOOK(item); 3069146213Sglebius 3070146213Sglebius#ifdef NETGRAPH_DEBUG 3071146213Sglebius mtx_lock(&ngq_mtx); 3072146213Sglebius TAILQ_REMOVE(&ng_itemlist, item, all); 3073146213Sglebius allocated--; 3074146213Sglebius mtx_unlock(&ngq_mtx); 3075146213Sglebius#endif 3076146213Sglebius uma_zfree(ng_qzone, item); 3077146213Sglebius} 3078146213Sglebius 3079146213Sglebius/************************************************************************ 308052419Sjulian Module routines 308152419Sjulian************************************************************************/ 308252419Sjulian 308352419Sjulian/* 308452419Sjulian * Handle the loading/unloading of a netgraph node type module 308552419Sjulian */ 308652419Sjulianint 308752419Sjulianng_mod_event(module_t mod, int event, void *data) 308852419Sjulian{ 308952419Sjulian struct ng_type *const type = data; 309052419Sjulian int s, error = 0; 309152419Sjulian 309252419Sjulian switch (event) { 309352419Sjulian case MOD_LOAD: 309452419Sjulian 309552419Sjulian /* Register new netgraph node type */ 309652419Sjulian s = splnet(); 309752419Sjulian if ((error = ng_newtype(type)) != 0) { 309852419Sjulian splx(s); 309952419Sjulian break; 310052419Sjulian } 310152419Sjulian 310252419Sjulian /* Call type specific code */ 310352419Sjulian if (type->mod_event != NULL) 310470700Sjulian if ((error = (*type->mod_event)(mod, event, data))) { 310572200Sbmilekic mtx_lock(&ng_typelist_mtx); 310671603Sjulian type->refs--; /* undo it */ 310752419Sjulian LIST_REMOVE(type, types); 310872200Sbmilekic mtx_unlock(&ng_typelist_mtx); 310970700Sjulian } 311052419Sjulian splx(s); 311152419Sjulian break; 311252419Sjulian 311352419Sjulian case MOD_UNLOAD: 311452419Sjulian s = splnet(); 311571603Sjulian if (type->refs > 1) { /* make sure no nodes exist! */ 311652419Sjulian error = EBUSY; 311771603Sjulian } else { 311871603Sjulian if (type->refs == 0) { 311971603Sjulian /* failed load, nothing to undo */ 312071603Sjulian splx(s); 312171603Sjulian break; 312271603Sjulian } 312352419Sjulian if (type->mod_event != NULL) { /* check with type */ 312452419Sjulian error = (*type->mod_event)(mod, event, data); 312552419Sjulian if (error != 0) { /* type refuses.. */ 312652419Sjulian splx(s); 312752419Sjulian break; 312852419Sjulian } 312952419Sjulian } 313072200Sbmilekic mtx_lock(&ng_typelist_mtx); 313152419Sjulian LIST_REMOVE(type, types); 313272200Sbmilekic mtx_unlock(&ng_typelist_mtx); 313352419Sjulian } 313452419Sjulian splx(s); 313552419Sjulian break; 313652419Sjulian 313752419Sjulian default: 313852419Sjulian if (type->mod_event != NULL) 313952419Sjulian error = (*type->mod_event)(mod, event, data); 314052419Sjulian else 3141132199Sphk error = EOPNOTSUPP; /* XXX ? */ 314252419Sjulian break; 314352419Sjulian } 314452419Sjulian return (error); 314552419Sjulian} 314652419Sjulian 314752419Sjulian/* 314852419Sjulian * Handle loading and unloading for this code. 314952419Sjulian * The only thing we need to link into is the NETISR strucure. 315052419Sjulian */ 315152419Sjulianstatic int 315252419Sjulianngb_mod_event(module_t mod, int event, void *data) 315352419Sjulian{ 3154146212Sglebius int error = 0; 315552419Sjulian 315652419Sjulian switch (event) { 315752419Sjulian case MOD_LOAD: 3158146212Sglebius /* Initialize everything. */ 3159168049Swkoszek NG_WORKLIST_LOCK_INIT(); 3160123278Struckman mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL, 3161123278Struckman MTX_DEF); 3162123278Struckman mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL, 3163123278Struckman MTX_DEF); 3164123278Struckman mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL, 3165123278Struckman MTX_DEF); 3166151974Sglebius mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL, 3167151974Sglebius MTX_DEF); 3168146212Sglebius#ifdef NETGRAPH_DEBUG 3169146212Sglebius mtx_init(&ngq_mtx, "netgraph item list mutex", NULL, 3170123278Struckman MTX_DEF); 3171146212Sglebius#endif 3172146212Sglebius ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item), 3173146212Sglebius NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0); 3174146212Sglebius uma_zone_set_max(ng_qzone, maxalloc); 3175141719Sglebius netisr_register(NETISR_NETGRAPH, (netisr_t *)ngintr, NULL, 3176141719Sglebius NETISR_MPSAFE); 317752419Sjulian break; 317852419Sjulian case MOD_UNLOAD: 3179167677Srwatson /* You can't unload it because an interface may be using it. */ 318052419Sjulian error = EBUSY; 318152419Sjulian break; 318252419Sjulian default: 318352419Sjulian error = EOPNOTSUPP; 318452419Sjulian break; 318552419Sjulian } 318652419Sjulian return (error); 318752419Sjulian} 318852419Sjulian 318952419Sjulianstatic moduledata_t netgraph_mod = { 319052419Sjulian "netgraph", 319152419Sjulian ngb_mod_event, 319252419Sjulian (NULL) 319352419Sjulian}; 3194139774SemaxDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE); 319572946SjulianSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family"); 319672946SjulianSYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,""); 319772946SjulianSYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, ""); 319852419Sjulian 319970784Sjulian#ifdef NETGRAPH_DEBUG 320070700Sjulianvoid 320170784Sjuliandumphook (hook_p hook, char *file, int line) 320270784Sjulian{ 320370784Sjulian printf("hook: name %s, %d refs, Last touched:\n", 320470784Sjulian _NG_HOOK_NAME(hook), hook->hk_refs); 320570784Sjulian printf(" Last active @ %s, line %d\n", 320670784Sjulian hook->lastfile, hook->lastline); 320770784Sjulian if (line) { 320870784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 320970784Sjulian } 321070784Sjulian} 321170784Sjulian 321270784Sjulianvoid 321370784Sjuliandumpnode(node_p node, char *file, int line) 321470784Sjulian{ 321570784Sjulian printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n", 321671047Sjulian _NG_NODE_ID(node), node->nd_type->name, 321770784Sjulian node->nd_numhooks, node->nd_flags, 321870784Sjulian node->nd_refs, node->nd_name); 321970784Sjulian printf(" Last active @ %s, line %d\n", 322070784Sjulian node->lastfile, node->lastline); 322170784Sjulian if (line) { 322270784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 322370784Sjulian } 322470784Sjulian} 322570784Sjulian 322670784Sjulianvoid 322770700Sjuliandumpitem(item_p item, char *file, int line) 322870700Sjulian{ 3229146212Sglebius printf(" ACTIVE item, last used at %s, line %d", 3230146212Sglebius item->lastfile, item->lastline); 3231146212Sglebius switch(item->el_flags & NGQF_TYPE) { 3232146212Sglebius case NGQF_DATA: 3233146212Sglebius printf(" - [data]\n"); 3234146212Sglebius break; 3235146212Sglebius case NGQF_MESG: 3236146212Sglebius printf(" - retaddr[%d]:\n", _NGI_RETADDR(item)); 3237146212Sglebius break; 3238146212Sglebius case NGQF_FN: 3239146212Sglebius printf(" - fn@%p (%p, %p, %p, %d (%x))\n", 3240146212Sglebius item->body.fn.fn_fn, 3241149735Sglebius _NGI_NODE(item), 3242149735Sglebius _NGI_HOOK(item), 3243146212Sglebius item->body.fn.fn_arg1, 3244146212Sglebius item->body.fn.fn_arg2, 3245146212Sglebius item->body.fn.fn_arg2); 3246146212Sglebius break; 3247146212Sglebius case NGQF_UNDEF: 3248146212Sglebius printf(" - UNDEFINED!\n"); 324952419Sjulian } 325070784Sjulian if (line) { 325170784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 3252149735Sglebius if (_NGI_NODE(item)) { 325370784Sjulian printf("node %p ([%x])\n", 3254149735Sglebius _NGI_NODE(item), ng_node2ID(_NGI_NODE(item))); 325570784Sjulian } 325670784Sjulian } 325770700Sjulian} 325852419Sjulian 325970784Sjulianstatic void 326070784Sjulianng_dumpitems(void) 326170784Sjulian{ 326270784Sjulian item_p item; 326370784Sjulian int i = 1; 326470784Sjulian TAILQ_FOREACH(item, &ng_itemlist, all) { 326570784Sjulian printf("[%d] ", i++); 326670784Sjulian dumpitem(item, NULL, 0); 326770784Sjulian } 326870784Sjulian} 326970784Sjulian 327070784Sjulianstatic void 327170784Sjulianng_dumpnodes(void) 327270784Sjulian{ 327370784Sjulian node_p node; 327470784Sjulian int i = 1; 3275131008Srwatson mtx_lock(&ng_nodelist_mtx); 327670784Sjulian SLIST_FOREACH(node, &ng_allnodes, nd_all) { 327770784Sjulian printf("[%d] ", i++); 327870784Sjulian dumpnode(node, NULL, 0); 327970784Sjulian } 3280131008Srwatson mtx_unlock(&ng_nodelist_mtx); 328170784Sjulian} 328270784Sjulian 328370784Sjulianstatic void 328470784Sjulianng_dumphooks(void) 328570784Sjulian{ 328670784Sjulian hook_p hook; 328770784Sjulian int i = 1; 3288131008Srwatson mtx_lock(&ng_nodelist_mtx); 328970784Sjulian SLIST_FOREACH(hook, &ng_allhooks, hk_all) { 329070784Sjulian printf("[%d] ", i++); 329170784Sjulian dumphook(hook, NULL, 0); 329270784Sjulian } 3293131008Srwatson mtx_unlock(&ng_nodelist_mtx); 329470784Sjulian} 329570784Sjulian 329670700Sjulianstatic int 329770700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS) 329870700Sjulian{ 329970700Sjulian int error; 330070700Sjulian int val; 330170700Sjulian int i; 330270700Sjulian 330370700Sjulian val = allocated; 330470700Sjulian i = 1; 3305170289Sdwmalone error = sysctl_handle_int(oidp, &val, 0, req); 330671047Sjulian if (error != 0 || req->newptr == NULL) 330771047Sjulian return (error); 330871047Sjulian if (val == 42) { 330970784Sjulian ng_dumpitems(); 331070784Sjulian ng_dumpnodes(); 331170784Sjulian ng_dumphooks(); 331270700Sjulian } 331371047Sjulian return (0); 331452419Sjulian} 331552419Sjulian 331671047SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW, 331771047Sjulian 0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items"); 331870784Sjulian#endif /* NETGRAPH_DEBUG */ 331970700Sjulian 332070700Sjulian 332170700Sjulian/*********************************************************************** 332270700Sjulian* Worklist routines 332370700Sjulian**********************************************************************/ 332470700Sjulian/* NETISR thread enters here */ 332552419Sjulian/* 332670700Sjulian * Pick a node off the list of nodes with work, 332770700Sjulian * try get an item to process off it. 332870700Sjulian * If there are no more, remove the node from the list. 332952419Sjulian */ 333070700Sjulianstatic void 333170700Sjulianngintr(void) 333252419Sjulian{ 333370700Sjulian item_p item; 333470700Sjulian node_p node = NULL; 333552419Sjulian 333670700Sjulian for (;;) { 3337168049Swkoszek NG_WORKLIST_LOCK(); 333870700Sjulian node = TAILQ_FIRST(&ng_worklist); 333970700Sjulian if (!node) { 3340168049Swkoszek NG_WORKLIST_UNLOCK(); 334170700Sjulian break; 334269922Sjulian } 3343132464Sjulian node->nd_flags &= ~NGF_WORKQ; 334470784Sjulian TAILQ_REMOVE(&ng_worklist, node, nd_work); 3345168049Swkoszek NG_WORKLIST_UNLOCK(); 3346154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist", 3347154275Sglebius __func__, node->nd_ID, node); 334870700Sjulian /* 334970700Sjulian * We have the node. We also take over the reference 335070700Sjulian * that the list had on it. 335170700Sjulian * Now process as much as you can, until it won't 335270700Sjulian * let you have another item off the queue. 335370700Sjulian * All this time, keep the reference 335470700Sjulian * that lets us be sure that the node still exists. 335570700Sjulian * Let the reference go at the last minute. 335671902Sjulian * ng_dequeue will put us back on the worklist 335771902Sjulian * if there is more too do. This may be of use if there 3358152451Sglebius * are Multiple Processors and multiple Net threads in the 335971902Sjulian * future. 336070700Sjulian */ 336170700Sjulian for (;;) { 3362151238Sglebius int rw; 3363151238Sglebius 3364168049Swkoszek NG_QUEUE_LOCK(&node->nd_input_queue); 3365151238Sglebius item = ng_dequeue(&node->nd_input_queue, &rw); 336670700Sjulian if (item == NULL) { 3367168049Swkoszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 336870700Sjulian break; /* go look for another node */ 336970700Sjulian } else { 3370168049Swkoszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 337174078Sjulian NGI_GET_NODE(item, node); /* zaps stored node */ 3372151238Sglebius ng_apply_item(node, item, rw); 337374078Sjulian NG_NODE_UNREF(node); 337470700Sjulian } 337570700Sjulian } 337673238Sjulian NG_NODE_UNREF(node); 337752419Sjulian } 337870700Sjulian} 337969922Sjulian 338070700Sjulianstatic void 338170700Sjulianng_worklist_remove(node_p node) 338270700Sjulian{ 3383154225Sglebius mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED); 3384154225Sglebius 3385168049Swkoszek NG_WORKLIST_LOCK(); 3386132464Sjulian if (node->nd_flags & NGF_WORKQ) { 3387132464Sjulian node->nd_flags &= ~NGF_WORKQ; 338870784Sjulian TAILQ_REMOVE(&ng_worklist, node, nd_work); 3389168049Swkoszek NG_WORKLIST_UNLOCK(); 339070784Sjulian NG_NODE_UNREF(node); 3391154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) removed from worklist", 3392154275Sglebius __func__, node->nd_ID, node); 339372979Sjulian } else { 3394168049Swkoszek NG_WORKLIST_UNLOCK(); 339570700Sjulian } 339670700Sjulian} 339770700Sjulian 339872979Sjulian/* 339972979Sjulian * XXX 340072979Sjulian * It's posible that a debugging NG_NODE_REF may need 340172979Sjulian * to be outside the mutex zone 340272979Sjulian */ 340370700Sjulianstatic void 340470700Sjulianng_setisr(node_p node) 340570700Sjulian{ 3406148236Sglebius 3407148266Sglebius mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED); 3408148236Sglebius 3409132464Sjulian if ((node->nd_flags & NGF_WORKQ) == 0) { 341069922Sjulian /* 341170700Sjulian * If we are not already on the work queue, 341270700Sjulian * then put us on. 341369922Sjulian */ 3414132464Sjulian node->nd_flags |= NGF_WORKQ; 3415168049Swkoszek NG_WORKLIST_LOCK(); 341670784Sjulian TAILQ_INSERT_TAIL(&ng_worklist, node, nd_work); 3417168049Swkoszek NG_WORKLIST_UNLOCK(); 341872979Sjulian NG_NODE_REF(node); /* XXX fafe in mutex? */ 3419154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__, 3420154275Sglebius node->nd_ID, node); 3421154225Sglebius } else 3422154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist", 3423154275Sglebius __func__, node->nd_ID, node); 342470700Sjulian schednetisr(NETISR_NETGRAPH); 342570700Sjulian} 342670700Sjulian 342770700Sjulian 342870700Sjulian/*********************************************************************** 342970700Sjulian* Externally useable functions to set up a queue item ready for sending 343070700Sjulian***********************************************************************/ 343170700Sjulian 343270784Sjulian#ifdef NETGRAPH_DEBUG 343370784Sjulian#define ITEM_DEBUG_CHECKS \ 343470700Sjulian do { \ 343571849Sjulian if (NGI_NODE(item) ) { \ 343670700Sjulian printf("item already has node"); \ 3437131933Smarcel kdb_enter("has node"); \ 343871849Sjulian NGI_CLR_NODE(item); \ 343970700Sjulian } \ 344071849Sjulian if (NGI_HOOK(item) ) { \ 344170700Sjulian printf("item already has hook"); \ 3442131933Smarcel kdb_enter("has hook"); \ 344371849Sjulian NGI_CLR_HOOK(item); \ 344470700Sjulian } \ 344570700Sjulian } while (0) 344670700Sjulian#else 344770784Sjulian#define ITEM_DEBUG_CHECKS 344870700Sjulian#endif 344970700Sjulian 345070700Sjulian/* 3451131374Sjulian * Put mbuf into the item. 345270700Sjulian * Hook and node references will be removed when the item is dequeued. 345370700Sjulian * (or equivalent) 345470700Sjulian * (XXX) Unsafe because no reference held by peer on remote node. 345570700Sjulian * remote node might go away in this timescale. 345670700Sjulian * We know the hooks can't go away because that would require getting 345770700Sjulian * a writer item on both nodes and we must have at least a reader 3458151973Sglebius * here to be able to do this. 345970700Sjulian * Note that the hook loaded is the REMOTE hook. 346070700Sjulian * 346170700Sjulian * This is possibly in the critical path for new data. 346270700Sjulian */ 346370700Sjulianitem_p 3464146281Sglebiusng_package_data(struct mbuf *m, int flags) 346570700Sjulian{ 346670700Sjulian item_p item; 346770700Sjulian 3468146281Sglebius if ((item = ng_getqblk(flags)) == NULL) { 346970700Sjulian NG_FREE_M(m); 347070700Sjulian return (NULL); 347170700Sjulian } 347270784Sjulian ITEM_DEBUG_CHECKS; 3473149505Sglebius item->el_flags = NGQF_DATA | NGQF_READER; 347470700Sjulian item->el_next = NULL; 347570700Sjulian NGI_M(item) = m; 347670700Sjulian return (item); 347770700Sjulian} 347870700Sjulian 347970700Sjulian/* 348070700Sjulian * Allocate a queue item and put items into it.. 348170700Sjulian * Evaluate the address as this will be needed to queue it and 348270700Sjulian * to work out what some of the fields should be. 348370700Sjulian * Hook and node references will be removed when the item is dequeued. 348470700Sjulian * (or equivalent) 348570700Sjulian */ 348670700Sjulianitem_p 3487146281Sglebiusng_package_msg(struct ng_mesg *msg, int flags) 348870700Sjulian{ 348970700Sjulian item_p item; 349070700Sjulian 3491146281Sglebius if ((item = ng_getqblk(flags)) == NULL) { 349271849Sjulian NG_FREE_MSG(msg); 349370700Sjulian return (NULL); 349469922Sjulian } 349570784Sjulian ITEM_DEBUG_CHECKS; 3496149505Sglebius /* Messages items count as writers unless explicitly exempted. */ 3497149505Sglebius if (msg->header.cmd & NGM_READONLY) 3498149505Sglebius item->el_flags = NGQF_MESG | NGQF_READER; 3499149505Sglebius else 3500149505Sglebius item->el_flags = NGQF_MESG | NGQF_WRITER; 350170700Sjulian item->el_next = NULL; 350270700Sjulian /* 350370700Sjulian * Set the current lasthook into the queue item 350470700Sjulian */ 350570700Sjulian NGI_MSG(item) = msg; 3506102244Sarchie NGI_RETADDR(item) = 0; 350770700Sjulian return (item); 350870700Sjulian} 350969922Sjulian 351070700Sjulian 351170700Sjulian 351271849Sjulian#define SET_RETADDR(item, here, retaddr) \ 351371047Sjulian do { /* Data or fn items don't have retaddrs */ \ 351471047Sjulian if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) { \ 351570700Sjulian if (retaddr) { \ 351670700Sjulian NGI_RETADDR(item) = retaddr; \ 351770700Sjulian } else { \ 351870700Sjulian /* \ 351970700Sjulian * The old return address should be ok. \ 352070700Sjulian * If there isn't one, use the address \ 352170700Sjulian * here. \ 352270700Sjulian */ \ 352370700Sjulian if (NGI_RETADDR(item) == 0) { \ 352470700Sjulian NGI_RETADDR(item) \ 352570700Sjulian = ng_node2ID(here); \ 352670700Sjulian } \ 352770700Sjulian } \ 352870700Sjulian } \ 352970700Sjulian } while (0) 353070700Sjulian 353170700Sjulianint 353270700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr) 353370700Sjulian{ 353471849Sjulian hook_p peer; 353571849Sjulian node_p peernode; 353670784Sjulian ITEM_DEBUG_CHECKS; 353770700Sjulian /* 353870700Sjulian * Quick sanity check.. 353970784Sjulian * Since a hook holds a reference on it's node, once we know 354070784Sjulian * that the peer is still connected (even if invalid,) we know 354170784Sjulian * that the peer node is present, though maybe invalid. 354270700Sjulian */ 354370700Sjulian if ((hook == NULL) 354470784Sjulian || NG_HOOK_NOT_VALID(hook) 354570784Sjulian || (NG_HOOK_PEER(hook) == NULL) 354670784Sjulian || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)) 354770784Sjulian || NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) { 354870700Sjulian NG_FREE_ITEM(item); 354971047Sjulian TRAP_ERROR(); 355073083Sjulian return (ENETDOWN); 355152419Sjulian } 355252419Sjulian 355370700Sjulian /* 355470700Sjulian * Transfer our interest to the other (peer) end. 355570700Sjulian */ 355671849Sjulian peer = NG_HOOK_PEER(hook); 355771849Sjulian NG_HOOK_REF(peer); 355871849Sjulian NGI_SET_HOOK(item, peer); 355971849Sjulian peernode = NG_PEER_NODE(hook); 356071849Sjulian NG_NODE_REF(peernode); 356171849Sjulian NGI_SET_NODE(item, peernode); 356279706Sjulian SET_RETADDR(item, here, retaddr); 356370700Sjulian return (0); 356470700Sjulian} 356552419Sjulian 356670700Sjulianint 3567152451Sglebiusng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr) 356870700Sjulian{ 3569152451Sglebius node_p dest = NULL; 357070700Sjulian hook_p hook = NULL; 3571152451Sglebius int error; 357270700Sjulian 357370784Sjulian ITEM_DEBUG_CHECKS; 357470700Sjulian /* 357570700Sjulian * Note that ng_path2noderef increments the reference count 357670700Sjulian * on the node for us if it finds one. So we don't have to. 357770700Sjulian */ 357870700Sjulian error = ng_path2noderef(here, address, &dest, &hook); 357970700Sjulian if (error) { 358070700Sjulian NG_FREE_ITEM(item); 358170784Sjulian return (error); 358252419Sjulian } 358371849Sjulian NGI_SET_NODE(item, dest); 358471849Sjulian if ( hook) { 358570784Sjulian NG_HOOK_REF(hook); /* don't let it go while on the queue */ 358671849Sjulian NGI_SET_HOOK(item, hook); 358771849Sjulian } 358871849Sjulian SET_RETADDR(item, here, retaddr); 358970700Sjulian return (0); 359070700Sjulian} 359152419Sjulian 359270700Sjulianint 359370700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr) 359470700Sjulian{ 359570700Sjulian node_p dest; 359670700Sjulian 359770784Sjulian ITEM_DEBUG_CHECKS; 359870700Sjulian /* 359970700Sjulian * Find the target node. 360070700Sjulian */ 360170700Sjulian dest = ng_ID2noderef(ID); /* GETS REFERENCE! */ 360270700Sjulian if (dest == NULL) { 360370700Sjulian NG_FREE_ITEM(item); 360471047Sjulian TRAP_ERROR(); 360570700Sjulian return(EINVAL); 360670700Sjulian } 360770700Sjulian /* Fill out the contents */ 360871849Sjulian NGI_SET_NODE(item, dest); 360971849Sjulian NGI_CLR_HOOK(item); 361071849Sjulian SET_RETADDR(item, here, retaddr); 361152419Sjulian return (0); 361252419Sjulian} 361352419Sjulian 361452419Sjulian/* 361570700Sjulian * special case to send a message to self (e.g. destroy node) 361670700Sjulian * Possibly indicate an arrival hook too. 361770700Sjulian * Useful for removing that hook :-) 361852419Sjulian */ 361970700Sjulianitem_p 362070700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg) 362152419Sjulian{ 362270700Sjulian item_p item; 362352419Sjulian 362470700Sjulian /* 362570700Sjulian * Find the target node. 362670700Sjulian * If there is a HOOK argument, then use that in preference 362770700Sjulian * to the address. 362870700Sjulian */ 3629146281Sglebius if ((item = ng_getqblk(NG_NOFLAGS)) == NULL) { 363071849Sjulian NG_FREE_MSG(msg); 363170700Sjulian return (NULL); 363252419Sjulian } 363370700Sjulian 363470700Sjulian /* Fill out the contents */ 3635149505Sglebius item->el_flags = NGQF_MESG | NGQF_WRITER; 363670700Sjulian item->el_next = NULL; 363770784Sjulian NG_NODE_REF(here); 363871849Sjulian NGI_SET_NODE(item, here); 363971849Sjulian if (hook) { 364070784Sjulian NG_HOOK_REF(hook); 364171849Sjulian NGI_SET_HOOK(item, hook); 364271849Sjulian } 364370700Sjulian NGI_MSG(item) = msg; 364470700Sjulian NGI_RETADDR(item) = ng_node2ID(here); 364570700Sjulian return (item); 364652419Sjulian} 364752419Sjulian 3648146281Sglebiusint 3649146180Sglebiusng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2, 3650146281Sglebius int flags) 365171047Sjulian{ 365271047Sjulian item_p item; 365371047Sjulian 3654146281Sglebius if ((item = ng_getqblk(flags)) == NULL) { 365571047Sjulian return (ENOMEM); 365671047Sjulian } 365771047Sjulian item->el_flags = NGQF_FN | NGQF_WRITER; 365873238Sjulian NG_NODE_REF(node); /* and one for the item */ 365971849Sjulian NGI_SET_NODE(item, node); 366071849Sjulian if (hook) { 366171047Sjulian NG_HOOK_REF(hook); 366271849Sjulian NGI_SET_HOOK(item, hook); 366371047Sjulian } 366471047Sjulian NGI_FN(item) = fn; 366571047Sjulian NGI_ARG1(item) = arg1; 366671047Sjulian NGI_ARG2(item) = arg2; 3667146281Sglebius return(ng_snd_item(item, flags)); 366871047Sjulian} 366971047Sjulian 3670152451Sglebius/* 367191711Sjulian * Official timeout routines for Netgraph nodes. 367291711Sjulian */ 367391711Sjulianstatic void 3674140852Sglebiusng_callout_trampoline(void *arg) 367591711Sjulian{ 367691711Sjulian item_p item = arg; 367791711Sjulian 367891711Sjulian ng_snd_item(item, 0); 367991711Sjulian} 368091711Sjulian 368191711Sjulian 3682137138Sglebiusint 3683138268Sglebiusng_callout(struct callout *c, node_p node, hook_p hook, int ticks, 368491711Sjulian ng_item_fn *fn, void * arg1, int arg2) 368591711Sjulian{ 3686149881Sglebius item_p item, oitem; 368791711Sjulian 3688146281Sglebius if ((item = ng_getqblk(NG_NOFLAGS)) == NULL) 3689137138Sglebius return (ENOMEM); 3690137138Sglebius 369191711Sjulian item->el_flags = NGQF_FN | NGQF_WRITER; 369291711Sjulian NG_NODE_REF(node); /* and one for the item */ 369391711Sjulian NGI_SET_NODE(item, node); 369491711Sjulian if (hook) { 369591711Sjulian NG_HOOK_REF(hook); 369691711Sjulian NGI_SET_HOOK(item, hook); 369791711Sjulian } 369891711Sjulian NGI_FN(item) = fn; 369991711Sjulian NGI_ARG1(item) = arg1; 370091711Sjulian NGI_ARG2(item) = arg2; 3701149881Sglebius oitem = c->c_arg; 3702149881Sglebius if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 && 3703149881Sglebius oitem != NULL) 3704149881Sglebius NG_FREE_ITEM(oitem); 3705137138Sglebius return (0); 370691711Sjulian} 370791711Sjulian 370891711Sjulian/* A special modified version of untimeout() */ 3709152451Sglebiusint 3710138268Sglebiusng_uncallout(struct callout *c, node_p node) 371191711Sjulian{ 371291711Sjulian item_p item; 3713137138Sglebius int rval; 3714149357Sglebius 3715149357Sglebius KASSERT(c != NULL, ("ng_uncallout: NULL callout")); 3716149357Sglebius KASSERT(node != NULL, ("ng_uncallout: NULL node")); 3717149357Sglebius 3718137230Sglebius rval = callout_stop(c); 3719137138Sglebius item = c->c_arg; 3720137138Sglebius /* Do an extra check */ 3721140852Sglebius if ((rval > 0) && (c->c_func == &ng_callout_trampoline) && 3722137138Sglebius (NGI_NODE(item) == node)) { 372391711Sjulian /* 372491711Sjulian * We successfully removed it from the queue before it ran 3725152451Sglebius * So now we need to unreference everything that was 372691711Sjulian * given extra references. (NG_FREE_ITEM does this). 372791711Sjulian */ 372891711Sjulian NG_FREE_ITEM(item); 372991711Sjulian } 3730149881Sglebius c->c_arg = NULL; 3731137138Sglebius 3732137138Sglebius return (rval); 373391711Sjulian} 373491711Sjulian 373570700Sjulian/* 373670700Sjulian * Set the address, if none given, give the node here. 373770700Sjulian */ 373870700Sjulianvoid 373970700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr) 374070700Sjulian{ 374170700Sjulian if (retaddr) { 374270700Sjulian NGI_RETADDR(item) = retaddr; 374370700Sjulian } else { 374470700Sjulian /* 374570700Sjulian * The old return address should be ok. 374670700Sjulian * If there isn't one, use the address here. 374770700Sjulian */ 374870700Sjulian NGI_RETADDR(item) = ng_node2ID(here); 374970700Sjulian } 375070700Sjulian} 375152419Sjulian 375270700Sjulian#define TESTING 375370700Sjulian#ifdef TESTING 375470700Sjulian/* just test all the macros */ 375570700Sjulianvoid 375670700Sjulianng_macro_test(item_p item); 375770700Sjulianvoid 375870700Sjulianng_macro_test(item_p item) 375970700Sjulian{ 376070700Sjulian node_p node = NULL; 376170700Sjulian hook_p hook = NULL; 376270700Sjulian struct mbuf *m; 376370700Sjulian struct ng_mesg *msg; 376470700Sjulian ng_ID_t retaddr; 376570700Sjulian int error; 376670700Sjulian 376770700Sjulian NGI_GET_M(item, m); 376870700Sjulian NGI_GET_MSG(item, msg); 376970700Sjulian retaddr = NGI_RETADDR(item); 3770131374Sjulian NG_SEND_DATA(error, hook, m, NULL); 377170700Sjulian NG_SEND_DATA_ONLY(error, hook, m); 377270700Sjulian NG_FWD_NEW_DATA(error, item, hook, m); 377370784Sjulian NG_FWD_ITEM_HOOK(error, item, hook); 377470700Sjulian NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr); 377570700Sjulian NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr); 377670700Sjulian NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr); 377770700Sjulian NG_FWD_MSG_HOOK(error, node, item, hook, retaddr); 377870700Sjulian} 377970700Sjulian#endif /* TESTING */ 378070700Sjulian 3781