ng_base.c revision 170180
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 170180 2007-06-01 09:20:57Z glebius $ 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 */ 139471849Sjulian ng_send_fn(node2, hook2, &ng_con_part2, NULL, 0); 139571047Sjulian 139671047Sjulian NG_HOOK_UNREF(hook); /* Let each hook go if it wants to */ 139771047Sjulian NG_HOOK_UNREF(hook2); 139871849Sjulian return (0); 139971047Sjulian} 140071047Sjulian 140171047Sjulian/* 140271047Sjulian * Make a peer and connect. 140371047Sjulian * We assume that the local node is locked. 140471047Sjulian * The new node probably doesn't need a lock until 140571047Sjulian * it has a hook, because it cannot really have any work until then, 140671047Sjulian * but we should think about it a bit more. 140771047Sjulian * 140871047Sjulian * The problem may come if the other node also fires up 140971047Sjulian * some hardware or a timer or some other source of activation, 141071047Sjulian * also it may already get a command msg via it's ID. 141171047Sjulian * 141271047Sjulian * We could use the same method as ng_con_nodes() but we'd have 1413152451Sglebius * to add ability to remove the node when failing. (Not hard, just 141471047Sjulian * make arg1 point to the node to remove). 141571047Sjulian * Unless of course we just ignore failure to connect and leave 141671047Sjulian * an unconnected node? 141771047Sjulian */ 141871047Sjulianstatic int 141971047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type) 142071047Sjulian{ 1421152451Sglebius node_p node2; 1422152451Sglebius hook_p hook1, hook2; 1423152451Sglebius int error; 142471047Sjulian 142571047Sjulian if ((error = ng_make_node(type, &node2))) { 142652419Sjulian return (error); 142752419Sjulian } 142870939Sjulian 142971047Sjulian if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */ 143071849Sjulian ng_rmnode(node2, NULL, NULL, 0); 143171047Sjulian return (error); 143271047Sjulian } 143371047Sjulian 143471047Sjulian if ((error = ng_add_hook(node2, name2, &hook2))) { 143571849Sjulian ng_rmnode(node2, NULL, NULL, 0); 143671047Sjulian ng_destroy_hook(hook1); 143771047Sjulian NG_HOOK_UNREF(hook1); 143871047Sjulian return (error); 143971047Sjulian } 144071047Sjulian 144170939Sjulian /* 144271047Sjulian * Actually link the two hooks together. 144371047Sjulian */ 144471047Sjulian hook1->hk_peer = hook2; 144571047Sjulian hook2->hk_peer = hook1; 144671047Sjulian 144771047Sjulian /* Each hook is referenced by the other */ 144871047Sjulian NG_HOOK_REF(hook1); 144971047Sjulian NG_HOOK_REF(hook2); 145071047Sjulian 145171047Sjulian /* Give each node the opportunity to veto the pending connection */ 145271047Sjulian if (hook1->hk_node->nd_type->connect) { 145371047Sjulian error = (*hook1->hk_node->nd_type->connect) (hook1); 145471047Sjulian } 145571047Sjulian 145671047Sjulian if ((error == 0) && hook2->hk_node->nd_type->connect) { 145771047Sjulian error = (*hook2->hk_node->nd_type->connect) (hook2); 145871047Sjulian 145971047Sjulian } 146071047Sjulian 146171047Sjulian /* 146270939Sjulian * drop the references we were holding on the two hooks. 146370939Sjulian */ 146471047Sjulian if (error) { 146571047Sjulian ng_destroy_hook(hook2); /* also zaps hook1 */ 146671849Sjulian ng_rmnode(node2, NULL, NULL, 0); 146771047Sjulian } else { 146871047Sjulian /* As a last act, allow the hooks to be used */ 146971047Sjulian hook1->hk_flags &= ~HK_INVALID; 147071047Sjulian hook2->hk_flags &= ~HK_INVALID; 147171047Sjulian } 147271047Sjulian NG_HOOK_UNREF(hook1); 147370939Sjulian NG_HOOK_UNREF(hook2); 147470939Sjulian return (error); 147552419Sjulian} 147671047Sjulian 147770700Sjulian/************************************************************************ 147870700Sjulian Utility routines to send self messages 147970700Sjulian************************************************************************/ 148070700Sjulian 148171849Sjulian/* Shut this node down as soon as everyone is clear of it */ 1482167677Srwatson/* Should add arg "immediately" to jump the queue */ 148370700Sjulianint 148471849Sjulianng_rmnode_self(node_p node) 148570700Sjulian{ 148671849Sjulian int error; 148752419Sjulian 148871849Sjulian if (node == &ng_deadnode) 148971849Sjulian return (0); 1490132464Sjulian node->nd_flags |= NGF_INVALID; 1491132464Sjulian if (node->nd_flags & NGF_CLOSING) 149271849Sjulian return (0); 149370700Sjulian 149471849Sjulian error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0); 149571849Sjulian return (error); 149670700Sjulian} 149770700Sjulian 149871849Sjulianstatic void 149971047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2) 150071047Sjulian{ 150171047Sjulian ng_destroy_hook(hook); 150271849Sjulian return ; 150371047Sjulian} 150471047Sjulian 150570935Sjulianint 150670935Sjulianng_rmhook_self(hook_p hook) 150770935Sjulian{ 150871047Sjulian int error; 150970935Sjulian node_p node = NG_HOOK_NODE(hook); 151070935Sjulian 151171047Sjulian if (node == &ng_deadnode) 151271047Sjulian return (0); 151371047Sjulian 151471047Sjulian error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0); 151571047Sjulian return (error); 151670935Sjulian} 151770935Sjulian 151870700Sjulian/*********************************************************************** 151952419Sjulian * Parse and verify a string of the form: <NODE:><PATH> 152052419Sjulian * 152152419Sjulian * Such a string can refer to a specific node or a specific hook 152252419Sjulian * on a specific node, depending on how you look at it. In the 152352419Sjulian * latter case, the PATH component must not end in a dot. 152452419Sjulian * 152552419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string 152652419Sjulian * of hook names separated by dots. This breaks out the original 152752419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp 152852419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to 152952419Sjulian * the final hook component of <PATH>, if any, otherwise NULL. 153052419Sjulian * 153152419Sjulian * This returns -1 if the path is malformed. The char ** are optional. 153270700Sjulian ***********************************************************************/ 153352419Sjulianint 153452419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 153552419Sjulian{ 1536152451Sglebius char *node, *path, *hook; 1537152451Sglebius int k; 153852419Sjulian 153952419Sjulian /* 154052419Sjulian * Extract absolute NODE, if any 154152419Sjulian */ 154252419Sjulian for (path = addr; *path && *path != ':'; path++); 154352419Sjulian if (*path) { 154452419Sjulian node = addr; /* Here's the NODE */ 154552419Sjulian *path++ = '\0'; /* Here's the PATH */ 154652419Sjulian 154752419Sjulian /* Node name must not be empty */ 154852419Sjulian if (!*node) 154952419Sjulian return -1; 155052419Sjulian 155152419Sjulian /* A name of "." is OK; otherwise '.' not allowed */ 155252419Sjulian if (strcmp(node, ".") != 0) { 155352419Sjulian for (k = 0; node[k]; k++) 155452419Sjulian if (node[k] == '.') 155552419Sjulian return -1; 155652419Sjulian } 155752419Sjulian } else { 155852419Sjulian node = NULL; /* No absolute NODE */ 155952419Sjulian path = addr; /* Here's the PATH */ 156052419Sjulian } 156152419Sjulian 156252419Sjulian /* Snoop for illegal characters in PATH */ 156352419Sjulian for (k = 0; path[k]; k++) 156452419Sjulian if (path[k] == ':') 156552419Sjulian return -1; 156652419Sjulian 156752419Sjulian /* Check for no repeated dots in PATH */ 156852419Sjulian for (k = 0; path[k]; k++) 156952419Sjulian if (path[k] == '.' && path[k + 1] == '.') 157052419Sjulian return -1; 157152419Sjulian 157252419Sjulian /* Remove extra (degenerate) dots from beginning or end of PATH */ 157352419Sjulian if (path[0] == '.') 157452419Sjulian path++; 157552419Sjulian if (*path && path[strlen(path) - 1] == '.') 157652419Sjulian path[strlen(path) - 1] = 0; 157752419Sjulian 157852419Sjulian /* If PATH has a dot, then we're not talking about a hook */ 157952419Sjulian if (*path) { 158052419Sjulian for (hook = path, k = 0; path[k]; k++) 158152419Sjulian if (path[k] == '.') { 158252419Sjulian hook = NULL; 158352419Sjulian break; 158452419Sjulian } 158552419Sjulian } else 158652419Sjulian path = hook = NULL; 158752419Sjulian 158852419Sjulian /* Done */ 158952419Sjulian if (nodep) 159052419Sjulian *nodep = node; 159152419Sjulian if (pathp) 159252419Sjulian *pathp = path; 159352419Sjulian if (hookp) 159452419Sjulian *hookp = hook; 159552419Sjulian return (0); 159652419Sjulian} 159752419Sjulian 159852419Sjulian/* 159952419Sjulian * Given a path, which may be absolute or relative, and a starting node, 160070700Sjulian * return the destination node. 160152419Sjulian */ 160252419Sjulianint 160370700Sjulianng_path2noderef(node_p here, const char *address, 160470700Sjulian node_p *destp, hook_p *lasthook) 160552419Sjulian{ 1606125028Sharti char fullpath[NG_PATHSIZ]; 160752419Sjulian char *nodename, *path, pbuf[2]; 160870700Sjulian node_p node, oldnode; 160952419Sjulian char *cp; 161059728Sjulian hook_p hook = NULL; 161152419Sjulian 161252419Sjulian /* Initialize */ 161370784Sjulian if (destp == NULL) { 161471047Sjulian TRAP_ERROR(); 161552419Sjulian return EINVAL; 161670784Sjulian } 161752419Sjulian *destp = NULL; 161852419Sjulian 161952419Sjulian /* Make a writable copy of address for ng_path_parse() */ 162052419Sjulian strncpy(fullpath, address, sizeof(fullpath) - 1); 162152419Sjulian fullpath[sizeof(fullpath) - 1] = '\0'; 162252419Sjulian 162352419Sjulian /* Parse out node and sequence of hooks */ 162452419Sjulian if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 162571047Sjulian TRAP_ERROR(); 162652419Sjulian return EINVAL; 162752419Sjulian } 162852419Sjulian if (path == NULL) { 162952419Sjulian pbuf[0] = '.'; /* Needs to be writable */ 163052419Sjulian pbuf[1] = '\0'; 163152419Sjulian path = pbuf; 163252419Sjulian } 163352419Sjulian 163470700Sjulian /* 163570700Sjulian * For an absolute address, jump to the starting node. 163670700Sjulian * Note that this holds a reference on the node for us. 163770700Sjulian * Don't forget to drop the reference if we don't need it. 163870700Sjulian */ 163952419Sjulian if (nodename) { 164070700Sjulian node = ng_name2noderef(here, nodename); 164152419Sjulian if (node == NULL) { 164271047Sjulian TRAP_ERROR(); 164352419Sjulian return (ENOENT); 164452419Sjulian } 164570700Sjulian } else { 164670700Sjulian if (here == NULL) { 164771047Sjulian TRAP_ERROR(); 164870700Sjulian return (EINVAL); 164970700Sjulian } 165052419Sjulian node = here; 165170784Sjulian NG_NODE_REF(node); 165270700Sjulian } 165352419Sjulian 165470700Sjulian /* 1655152451Sglebius * Now follow the sequence of hooks 165670700Sjulian * XXX 165770700Sjulian * We actually cannot guarantee that the sequence 165870700Sjulian * is not being demolished as we crawl along it 165970700Sjulian * without extra-ordinary locking etc. 166070700Sjulian * So this is a bit dodgy to say the least. 166170700Sjulian * We can probably hold up some things by holding 166270700Sjulian * the nodelist mutex for the time of this 166370700Sjulian * crawl if we wanted.. At least that way we wouldn't have to 1664167677Srwatson * worry about the nodes disappearing, but the hooks would still 166570700Sjulian * be a problem. 166670700Sjulian */ 166752419Sjulian for (cp = path; node != NULL && *cp != '\0'; ) { 166852419Sjulian char *segment; 166952419Sjulian 167052419Sjulian /* 167152419Sjulian * Break out the next path segment. Replace the dot we just 167252419Sjulian * found with a NUL; "cp" points to the next segment (or the 167352419Sjulian * NUL at the end). 167452419Sjulian */ 167552419Sjulian for (segment = cp; *cp != '\0'; cp++) { 167652419Sjulian if (*cp == '.') { 167752419Sjulian *cp++ = '\0'; 167852419Sjulian break; 167952419Sjulian } 168052419Sjulian } 168152419Sjulian 168252419Sjulian /* Empty segment */ 168352419Sjulian if (*segment == '\0') 168452419Sjulian continue; 168552419Sjulian 168652419Sjulian /* We have a segment, so look for a hook by that name */ 168754096Sarchie hook = ng_findhook(node, segment); 168852419Sjulian 168952419Sjulian /* Can't get there from here... */ 169052419Sjulian if (hook == NULL 169170784Sjulian || NG_HOOK_PEER(hook) == NULL 169270784Sjulian || NG_HOOK_NOT_VALID(hook) 169370784Sjulian || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) { 169471047Sjulian TRAP_ERROR(); 169570784Sjulian NG_NODE_UNREF(node); 1696152451Sglebius#if 0 169770784Sjulian printf("hooknotvalid %s %s %d %d %d %d ", 169870784Sjulian path, 169970784Sjulian segment, 170070784Sjulian hook == NULL, 1701152451Sglebius NG_HOOK_PEER(hook) == NULL, 1702152451Sglebius NG_HOOK_NOT_VALID(hook), 1703152451Sglebius NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))); 170470784Sjulian#endif 170552419Sjulian return (ENOENT); 170652419Sjulian } 170752419Sjulian 170870700Sjulian /* 1709152451Sglebius * Hop on over to the next node 171070700Sjulian * XXX 1711152451Sglebius * Big race conditions here as hooks and nodes go away 171270700Sjulian * *** Idea.. store an ng_ID_t in each hook and use that 171370700Sjulian * instead of the direct hook in this crawl? 171470700Sjulian */ 171570700Sjulian oldnode = node; 171670784Sjulian if ((node = NG_PEER_NODE(hook))) 171770784Sjulian NG_NODE_REF(node); /* XXX RACE */ 171870784Sjulian NG_NODE_UNREF(oldnode); /* XXX another race */ 171970784Sjulian if (NG_NODE_NOT_VALID(node)) { 172070784Sjulian NG_NODE_UNREF(node); /* XXX more races */ 172170700Sjulian node = NULL; 172270700Sjulian } 172352419Sjulian } 172452419Sjulian 172552419Sjulian /* If node somehow missing, fail here (probably this is not needed) */ 172652419Sjulian if (node == NULL) { 172771047Sjulian TRAP_ERROR(); 172852419Sjulian return (ENXIO); 172952419Sjulian } 173052419Sjulian 173152419Sjulian /* Done */ 173252419Sjulian *destp = node; 173359900Sarchie if (lasthook != NULL) 173470784Sjulian *lasthook = (hook ? NG_HOOK_PEER(hook) : NULL); 173552419Sjulian return (0); 173652419Sjulian} 173752419Sjulian 173870700Sjulian/***************************************************************\ 173970700Sjulian* Input queue handling. 174070700Sjulian* All activities are submitted to the node via the input queue 174170700Sjulian* which implements a multiple-reader/single-writer gate. 1742167677Srwatson* Items which cannot be handled immediately are queued. 174370700Sjulian* 174470700Sjulian* read-write queue locking inline functions * 174570700Sjulian\***************************************************************/ 174670700Sjulian 1747151238Sglebiusstatic __inline item_p ng_dequeue(struct ng_queue * ngq, int *rw); 174870700Sjulianstatic __inline item_p ng_acquire_read(struct ng_queue * ngq, 174970700Sjulian item_p item); 175070700Sjulianstatic __inline item_p ng_acquire_write(struct ng_queue * ngq, 175170700Sjulian item_p item); 175270700Sjulianstatic __inline void ng_leave_read(struct ng_queue * ngq); 175370700Sjulianstatic __inline void ng_leave_write(struct ng_queue * ngq); 175470700Sjulianstatic __inline void ng_queue_rw(struct ng_queue * ngq, 175570700Sjulian item_p item, int rw); 175670700Sjulian 175752419Sjulian/* 175870700Sjulian * Definition of the bits fields in the ng_queue flag word. 175970700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle 176070700Sjulian * with them. 176170700Sjulian * 176271902Sjulian * The ordering here may be important! don't shuffle these. 176352419Sjulian */ 176470700Sjulian/*- 176570700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet) 176670700Sjulian | 176770700Sjulian V 176870700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+ 1769151973Sglebius | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1770151973Sglebius | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A| 1771151973Sglebius | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W| 177270700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+ 1773151973Sglebius \___________________________ ____________________________/ | | 1774151973Sglebius V | | 1775151973Sglebius [active reader count] | | 177670700Sjulian | | 1777151973Sglebius Operation Pending -------------------------------+ | 177870700Sjulian | 1779151973Sglebius Active Writer ---------------------------------------+ 178071902Sjulian 178171902Sjulian 178270700Sjulian*/ 1783151973Sglebius#define WRITER_ACTIVE 0x00000001 1784151973Sglebius#define OP_PENDING 0x00000002 1785151973Sglebius#define READER_INCREMENT 0x00000004 1786151973Sglebius#define READER_MASK 0xfffffffc /* Not valid if WRITER_ACTIVE is set */ 1787151973Sglebius#define SAFETY_BARRIER 0x00100000 /* 128K items queued should be enough */ 178871902Sjulian 178971902Sjulian/* Defines of more elaborate states on the queue */ 1790151973Sglebius/* Mask of bits a new read cares about */ 1791151973Sglebius#define NGQ_RMASK (WRITER_ACTIVE|OP_PENDING) 179271902Sjulian 1793151973Sglebius/* Mask of bits a new write cares about */ 179471902Sjulian#define NGQ_WMASK (NGQ_RMASK|READER_MASK) 179571902Sjulian 1796151973Sglebius/* Test to decide if there is something on the queue. */ 1797151973Sglebius#define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING) 179871902Sjulian 1799151973Sglebius/* How to decide what the next queued item is. */ 1800151973Sglebius#define HEAD_IS_READER(QP) NGI_QUEUED_READER((QP)->queue) 1801151973Sglebius#define HEAD_IS_WRITER(QP) NGI_QUEUED_WRITER((QP)->queue) /* notused */ 1802151973Sglebius 1803151973Sglebius/* Read the status to decide if the next item on the queue can now run. */ 1804151973Sglebius#define QUEUED_READER_CAN_PROCEED(QP) \ 1805151973Sglebius (((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0) 1806151973Sglebius#define QUEUED_WRITER_CAN_PROCEED(QP) \ 1807151973Sglebius (((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0) 1808151973Sglebius 180971902Sjulian/* Is there a chance of getting ANY work off the queue? */ 1810151973Sglebius#define NEXT_QUEUED_ITEM_CAN_PROCEED(QP) \ 1811151973Sglebius (QUEUE_ACTIVE(QP) && \ 1812151973Sglebius ((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) : \ 1813151973Sglebius QUEUED_WRITER_CAN_PROCEED(QP))) 181471902Sjulian 1815151973Sglebius 1816151238Sglebius#define NGQRW_R 0 1817151238Sglebius#define NGQRW_W 1 1818151238Sglebius 181970700Sjulian/* 182070700Sjulian * Taking into account the current state of the queue and node, possibly take 182170700Sjulian * the next entry off the queue and return it. Return NULL if there was 182270700Sjulian * nothing we could return, either because there really was nothing there, or 182370700Sjulian * because the node was in a state where it cannot yet process the next item 182470700Sjulian * on the queue. 182570700Sjulian * 182670700Sjulian * This MUST MUST MUST be called with the mutex held. 182770700Sjulian */ 182870700Sjulianstatic __inline item_p 1829151238Sglebiusng_dequeue(struct ng_queue *ngq, int *rw) 183070700Sjulian{ 183170700Sjulian item_p item; 183270700Sjulian u_int add_arg; 183371902Sjulian 1834139039Sglebius mtx_assert(&ngq->q_mtx, MA_OWNED); 1835152451Sglebius /* 1836151973Sglebius * If there is nothing queued, then just return. 1837151973Sglebius * No point in continuing. 1838151973Sglebius * XXXGL: assert this? 1839151973Sglebius */ 1840151973Sglebius if (!QUEUE_ACTIVE(ngq)) { 1841154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; " 1842154275Sglebius "queue flags 0x%lx", __func__, 1843154275Sglebius ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags); 1844151973Sglebius return (NULL); 1845151973Sglebius } 1846139039Sglebius 1847151973Sglebius /* 1848151973Sglebius * From here, we can assume there is a head item. 1849151973Sglebius * We need to find out what it is and if it can be dequeued, given 1850151973Sglebius * the current state of the node. 1851151973Sglebius */ 1852151973Sglebius if (HEAD_IS_READER(ngq)) { 1853151973Sglebius if (!QUEUED_READER_CAN_PROCEED(ngq)) { 1854151973Sglebius /* 1855151973Sglebius * It's a reader but we can't use it. 1856151973Sglebius * We are stalled so make sure we don't 1857151973Sglebius * get called again until something changes. 1858151973Sglebius */ 1859151973Sglebius ng_worklist_remove(ngq->q_node); 1860154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) queued reader " 1861154275Sglebius "can't proceed; queue flags 0x%lx", __func__, 1862154275Sglebius ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags); 1863151973Sglebius return (NULL); 1864151973Sglebius } 186570700Sjulian /* 186671902Sjulian * Head of queue is a reader and we have no write active. 1867152451Sglebius * We don't care how many readers are already active. 1868151973Sglebius * Add the correct increment for the reader count. 186970700Sjulian */ 1870151973Sglebius add_arg = READER_INCREMENT; 1871151238Sglebius *rw = NGQRW_R; 1872151973Sglebius } else if (QUEUED_WRITER_CAN_PROCEED(ngq)) { 187370700Sjulian /* 187470700Sjulian * There is a pending write, no readers and no active writer. 187570700Sjulian * This means we can go ahead with the pending writer. Note 187670700Sjulian * the fact that we now have a writer, ready for when we take 187770700Sjulian * it off the queue. 187870700Sjulian * 187970700Sjulian * We don't need to worry about a possible collision with the 188070700Sjulian * fasttrack reader. 188170700Sjulian * 188270700Sjulian * The fasttrack thread may take a long time to discover that we 188370700Sjulian * are running so we would have an inconsistent state in the 188470700Sjulian * flags for a while. Since we ignore the reader count 188570700Sjulian * entirely when the WRITER_ACTIVE flag is set, this should 188670700Sjulian * not matter (in fact it is defined that way). If it tests 1887151973Sglebius * the flag before this operation, the OP_PENDING flag 188870700Sjulian * will make it fail, and if it tests it later, the 188971902Sjulian * WRITER_ACTIVE flag will do the same. If it is SO slow that 189070700Sjulian * we have actually completed the operation, and neither flag 1891151973Sglebius * is set by the time that it tests the flags, then it is 1892151973Sglebius * actually ok for it to continue. If it completes and we've 1893151973Sglebius * finished and the read pending is set it still fails. 189470700Sjulian * 189570700Sjulian * So we can just ignore it, as long as we can ensure that the 189670700Sjulian * transition from WRITE_PENDING state to the WRITER_ACTIVE 189770700Sjulian * state is atomic. 189870700Sjulian * 189970700Sjulian * After failing, first it will be held back by the mutex, then 190070700Sjulian * when it can proceed, it will queue its request, then it 190170700Sjulian * would arrive at this function. Usually it will have to 190271902Sjulian * leave empty handed because the ACTIVE WRITER bit will be 190370700Sjulian * set. 190471902Sjulian * 1905151973Sglebius * Adjust the flags for the new active writer. 190670700Sjulian */ 1907151973Sglebius add_arg = WRITER_ACTIVE; 1908151238Sglebius *rw = NGQRW_W; 190970700Sjulian /* 191070700Sjulian * We want to write "active writer, no readers " Now go make 191170700Sjulian * it true. In fact there may be a number in the readers 191270700Sjulian * count but we know it is not true and will be fixed soon. 191370700Sjulian * We will fix the flags for the next pending entry in a 191470700Sjulian * moment. 191570700Sjulian */ 191670700Sjulian } else { 191770700Sjulian /* 191870700Sjulian * We can't dequeue anything.. return and say so. Probably we 191970700Sjulian * have a write pending and the readers count is non zero. If 192070700Sjulian * we got here because a reader hit us just at the wrong 192170700Sjulian * moment with the fasttrack code, and put us in a strange 1922151973Sglebius * state, then it will be coming through in just a moment, 1923151973Sglebius * (just as soon as we release the mutex) and keep things 1924151973Sglebius * moving. 1925151973Sglebius * Make sure we remove ourselves from the work queue. It 1926151973Sglebius * would be a waste of effort to do all this again. 192770700Sjulian */ 192871902Sjulian ng_worklist_remove(ngq->q_node); 1929154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) can't dequeue anything; " 1930154275Sglebius "queue flags 0x%lx", __func__, 1931154275Sglebius ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags); 1932151973Sglebius return (NULL); 193370700Sjulian } 193452419Sjulian 193570700Sjulian /* 193670700Sjulian * Now we dequeue the request (whatever it may be) and correct the 193770700Sjulian * pending flags and the next and last pointers. 193870700Sjulian */ 193970700Sjulian item = ngq->queue; 194070700Sjulian ngq->queue = item->el_next; 1941154275Sglebius CTR6(KTR_NET, "%20s: node [%x] (%p) dequeued item %p with flags 0x%lx; " 1942154275Sglebius "queue flags 0x%lx", __func__, 1943154275Sglebius ngq->q_node->nd_ID,ngq->q_node, item, item->el_flags, ngq->q_flags); 194470700Sjulian if (ngq->last == &(item->el_next)) { 194570700Sjulian /* 194670700Sjulian * that was the last entry in the queue so set the 'last 1947151973Sglebius * pointer up correctly and make sure the pending flag is 194870700Sjulian * clear. 194970700Sjulian */ 1950151973Sglebius add_arg += -OP_PENDING; 195170700Sjulian ngq->last = &(ngq->queue); 195270700Sjulian /* 195371902Sjulian * Whatever flag was set will be cleared and 195471902Sjulian * the new acive field will be set by the add as well, 195571902Sjulian * so we don't need to change add_arg. 195671902Sjulian * But we know we don't need to be on the work list. 195770700Sjulian */ 195871902Sjulian atomic_add_long(&ngq->q_flags, add_arg); 195971902Sjulian ng_worklist_remove(ngq->q_node); 196070700Sjulian } else { 1961152451Sglebius /* 1962151973Sglebius * Since there is still something on the queue 1963151973Sglebius * we don't need to change the PENDING flag. 196471902Sjulian */ 196571902Sjulian atomic_add_long(&ngq->q_flags, add_arg); 196671902Sjulian /* 196771902Sjulian * If we see more doable work, make sure we are 196871902Sjulian * on the work queue. 196971902Sjulian */ 1970151973Sglebius if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) { 197171902Sjulian ng_setisr(ngq->q_node); 197271902Sjulian } 197370700Sjulian } 1974154275Sglebius CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; " 1975154275Sglebius "queue flags 0x%lx", __func__, 1976154275Sglebius ngq->q_node->nd_ID, ngq->q_node, item, *rw ? "WRITER" : "READER" , 1977154225Sglebius ngq->q_flags); 197870700Sjulian return (item); 197970700Sjulian} 198052419Sjulian 198170700Sjulian/* 198270700Sjulian * Queue a packet to be picked up by someone else. 198370700Sjulian * We really don't care who, but we can't or don't want to hang around 198470700Sjulian * to process it ourselves. We are probably an interrupt routine.. 1985151973Sglebius * If the queue could be run, flag the netisr handler to start. 198670700Sjulian */ 198770700Sjulianstatic __inline void 198870700Sjulianng_queue_rw(struct ng_queue * ngq, item_p item, int rw) 198970700Sjulian{ 1990139039Sglebius mtx_assert(&ngq->q_mtx, MA_OWNED); 1991139039Sglebius 1992151973Sglebius if (rw == NGQRW_W) 1993151973Sglebius NGI_SET_WRITER(item); 1994151973Sglebius else 1995151973Sglebius NGI_SET_READER(item); 199670700Sjulian item->el_next = NULL; /* maybe not needed */ 199770700Sjulian *ngq->last = item; 1998154275Sglebius CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__, 1999154275Sglebius ngq->q_node->nd_ID, ngq->q_node, item, rw ? "WRITER" : "READER" ); 200070700Sjulian /* 200170700Sjulian * If it was the first item in the queue then we need to 200270700Sjulian * set the last pointer and the type flags. 200370700Sjulian */ 2004154225Sglebius if (ngq->last == &(ngq->queue)) { 2005151973Sglebius atomic_add_long(&ngq->q_flags, OP_PENDING); 2006154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__, 2007154275Sglebius ngq->q_node->nd_ID, ngq->q_node); 2008154225Sglebius } 2009151973Sglebius 201070700Sjulian ngq->last = &(item->el_next); 2011151973Sglebius /* 2012151973Sglebius * We can take the worklist lock with the node locked 2013151973Sglebius * BUT NOT THE REVERSE! 2014151973Sglebius */ 2015151973Sglebius if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2016151973Sglebius ng_setisr(ngq->q_node); 201770700Sjulian} 201852419Sjulian 201970700Sjulian 202052419Sjulian/* 202170700Sjulian * This function 'cheats' in that it first tries to 'grab' the use of the 202270700Sjulian * node, without going through the mutex. We can do this becasue of the 202370700Sjulian * semantics of the lock. The semantics include a clause that says that the 202470700Sjulian * value of the readers count is invalid if the WRITER_ACTIVE flag is set. It 202570700Sjulian * also says that the WRITER_ACTIVE flag cannot be set if the readers count 202670700Sjulian * is not zero. Note that this talks about what is valid to SET the 202770700Sjulian * WRITER_ACTIVE flag, because from the moment it is set, the value if the 202870700Sjulian * reader count is immaterial, and not valid. The two 'pending' flags have a 202970700Sjulian * similar effect, in that If they are orthogonal to the two active fields in 203070700Sjulian * how they are set, but if either is set, the attempted 'grab' need to be 203170700Sjulian * backed out because there is earlier work, and we maintain ordering in the 203270700Sjulian * queue. The result of this is that the reader request can try obtain use of 203370700Sjulian * the node with only a single atomic addition, and without any of the mutex 203470700Sjulian * overhead. If this fails the operation degenerates to the same as for other 203570700Sjulian * cases. 203670700Sjulian * 203752419Sjulian */ 203870700Sjulianstatic __inline item_p 203970700Sjulianng_acquire_read(struct ng_queue *ngq, item_p item) 204052419Sjulian{ 2041151974Sglebius KASSERT(ngq != &ng_deadnode.nd_input_queue, 2042151974Sglebius ("%s: working on deadnode", __func__)); 204352419Sjulian 204470700Sjulian /* ######### Hack alert ######### */ 204570700Sjulian atomic_add_long(&ngq->q_flags, READER_INCREMENT); 204671902Sjulian if ((ngq->q_flags & NGQ_RMASK) == 0) { 204770700Sjulian /* Successfully grabbed node */ 2048154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) fast acquired item %p", 2049154275Sglebius __func__, ngq->q_node->nd_ID, ngq->q_node, item); 205070700Sjulian return (item); 205170700Sjulian } 205270700Sjulian /* undo the damage if we didn't succeed */ 205370700Sjulian atomic_subtract_long(&ngq->q_flags, READER_INCREMENT); 205470700Sjulian 205570700Sjulian /* ######### End Hack alert ######### */ 2056168049Swkoszek NG_QUEUE_LOCK(ngq); 205769922Sjulian /* 205870700Sjulian * Try again. Another processor (or interrupt for that matter) may 205970700Sjulian * have removed the last queued item that was stopping us from 206070700Sjulian * running, between the previous test, and the moment that we took 206170700Sjulian * the mutex. (Or maybe a writer completed.) 2062151973Sglebius * Even if another fast-track reader hits during this period 2063151973Sglebius * we don't care as multiple readers is OK. 206469922Sjulian */ 206571902Sjulian if ((ngq->q_flags & NGQ_RMASK) == 0) { 206670700Sjulian atomic_add_long(&ngq->q_flags, READER_INCREMENT); 2067168049Swkoszek NG_QUEUE_UNLOCK(ngq); 2068154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) slow acquired item %p", 2069154275Sglebius __func__, ngq->q_node->nd_ID, ngq->q_node, item); 207070700Sjulian return (item); 207170700Sjulian } 207270700Sjulian 207370700Sjulian /* 207470700Sjulian * and queue the request for later. 207570700Sjulian */ 207670700Sjulian ng_queue_rw(ngq, item, NGQRW_R); 2077168049Swkoszek NG_QUEUE_UNLOCK(ngq); 207870700Sjulian 2079148236Sglebius return (NULL); 208070700Sjulian} 208170700Sjulian 208270700Sjulianstatic __inline item_p 208370700Sjulianng_acquire_write(struct ng_queue *ngq, item_p item) 208470700Sjulian{ 2085151974Sglebius KASSERT(ngq != &ng_deadnode.nd_input_queue, 2086151974Sglebius ("%s: working on deadnode", __func__)); 2087151974Sglebius 208870700Sjulianrestart: 2089168049Swkoszek NG_QUEUE_LOCK(ngq); 209070700Sjulian /* 209170700Sjulian * If there are no readers, no writer, and no pending packets, then 209270700Sjulian * we can just go ahead. In all other situations we need to queue the 209370700Sjulian * request 209470700Sjulian */ 209571902Sjulian if ((ngq->q_flags & NGQ_WMASK) == 0) { 2096151973Sglebius /* collision could happen *HERE* */ 209770700Sjulian atomic_add_long(&ngq->q_flags, WRITER_ACTIVE); 2098168049Swkoszek NG_QUEUE_UNLOCK(ngq); 209970700Sjulian if (ngq->q_flags & READER_MASK) { 210070700Sjulian /* Collision with fast-track reader */ 210171902Sjulian atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE); 210270700Sjulian goto restart; 210369922Sjulian } 2104154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", 2105154275Sglebius __func__, ngq->q_node->nd_ID, ngq->q_node, item); 210670700Sjulian return (item); 210752419Sjulian } 210852419Sjulian 210970700Sjulian /* 211070700Sjulian * and queue the request for later. 211170700Sjulian */ 211270700Sjulian ng_queue_rw(ngq, item, NGQRW_W); 2113168049Swkoszek NG_QUEUE_UNLOCK(ngq); 211470700Sjulian 2115148236Sglebius return (NULL); 211670700Sjulian} 211770700Sjulian 2118167385Sjulian#if 0 2119167385Sjulianstatic __inline item_p 2120167385Sjulianng_upgrade_write(struct ng_queue *ngq, item_p item) 2121167385Sjulian{ 2122167385Sjulian KASSERT(ngq != &ng_deadnode.nd_input_queue, 2123167385Sjulian ("%s: working on deadnode", __func__)); 2124167385Sjulian 2125167385Sjulian NGI_SET_WRITER(item); 2126167385Sjulian 2127167385Sjulian mtx_lock_spin(&(ngq->q_mtx)); 2128167385Sjulian 2129167385Sjulian /* 2130167385Sjulian * There will never be no readers as we are there ourselves. 2131167385Sjulian * Set the WRITER_ACTIVE flags ASAP to block out fast track readers. 2132167385Sjulian * The caller we are running from will call ng_leave_read() 2133167385Sjulian * soon, so we must account for that. We must leave again with the 2134167385Sjulian * READER lock. If we find other readers, then 2135167385Sjulian * queue the request for later. However "later" may be rignt now 2136167385Sjulian * if there are no readers. We don't really care if there are queued 2137167385Sjulian * items as we will bypass them anyhow. 2138167385Sjulian */ 2139167385Sjulian atomic_add_long(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT); 2140167385Sjulian if (ngq->q_flags & (NGQ_WMASK & ~OP_PENDING) == WRITER_ACTIVE) { 2141167385Sjulian mtx_unlock_spin(&(ngq->q_mtx)); 2142167385Sjulian 2143167385Sjulian /* It's just us, act on the item. */ 2144167385Sjulian /* will NOT drop writer lock when done */ 2145167385Sjulian ng_apply_item(node, item, 0); 2146167385Sjulian 2147167385Sjulian /* 2148167385Sjulian * Having acted on the item, atomically 2149167385Sjulian * down grade back to READER and finish up 2150167385Sjulian */ 2151167385Sjulian atomic_add_long(&ngq->q_flags, 2152167385Sjulian READER_INCREMENT - WRITER_ACTIVE); 2153167385Sjulian 2154167385Sjulian /* Our caller will call ng_leave_read() */ 2155167385Sjulian return; 2156167385Sjulian } 2157167385Sjulian /* 2158167385Sjulian * It's not just us active, so queue us AT THE HEAD. 2159167385Sjulian * "Why?" I hear you ask. 2160167385Sjulian * Put us at the head of the queue as we've already been 2161167385Sjulian * through it once. If there is nothing else waiting, 2162167385Sjulian * set the correct flags. 2163167385Sjulian */ 2164167385Sjulian if ((item->el_next = ngq->queue) == NULL) { 2165167385Sjulian /* 2166167385Sjulian * Set up the "last" pointer. 2167167385Sjulian * We are the only (and thus last) item 2168167385Sjulian */ 2169167385Sjulian ngq->last = &(item->el_next); 2170167385Sjulian 2171167385Sjulian /* We've gone from, 0 to 1 item in the queue */ 2172167385Sjulian atomic_add_long(&ngq->q_flags, OP_PENDING); 2173167385Sjulian 2174167385Sjulian CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__, 2175167385Sjulian ngq->q_node->nd_ID, ngq->q_node); 2176167385Sjulian }; 2177167385Sjulian ngq->queue = item; 2178167385Sjulian CTR5(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER", 2179167385Sjulian __func__, ngq->q_node->nd_ID, ngq->q_node, item ); 2180167385Sjulian 2181167385Sjulian /* Reverse what we did above. That downgrades us back to reader */ 2182167385Sjulian atomic_add_long(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE); 2183167385Sjulian if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2184167385Sjulian ng_setisr(ngq->q_node); 2185167385Sjulian mtx_unlock_spin(&(ngq->q_mtx)); 2186167385Sjulian 2187167385Sjulian return; 2188167385Sjulian} 2189167385Sjulian 2190167385Sjulian#endif 2191167385Sjulian 219270700Sjulianstatic __inline void 219370700Sjulianng_leave_read(struct ng_queue *ngq) 219470700Sjulian{ 219570700Sjulian atomic_subtract_long(&ngq->q_flags, READER_INCREMENT); 219670700Sjulian} 219770700Sjulian 219870700Sjulianstatic __inline void 219970700Sjulianng_leave_write(struct ng_queue *ngq) 220070700Sjulian{ 220170700Sjulian atomic_subtract_long(&ngq->q_flags, WRITER_ACTIVE); 220270700Sjulian} 220370700Sjulian 220470700Sjulianstatic void 220570700Sjulianng_flush_input_queue(struct ng_queue * ngq) 220670700Sjulian{ 220770700Sjulian item_p item; 2208151973Sglebius 2209168049Swkoszek NG_QUEUE_LOCK(ngq); 2210151973Sglebius while (ngq->queue) { 221170700Sjulian item = ngq->queue; 221270700Sjulian ngq->queue = item->el_next; 221370700Sjulian if (ngq->last == &(item->el_next)) { 221470700Sjulian ngq->last = &(ngq->queue); 2215151973Sglebius atomic_add_long(&ngq->q_flags, -OP_PENDING); 221670700Sjulian } 2217168049Swkoszek NG_QUEUE_UNLOCK(ngq); 221870700Sjulian 2219151973Sglebius /* If the item is supplying a callback, call it with an error */ 2220151283Sglebius if (item->apply != NULL) { 2221151283Sglebius (item->apply)(item->context, ENOENT); 2222151283Sglebius item->apply = NULL; 2223151283Sglebius } 2224132369Sjulian NG_FREE_ITEM(item); 2225168049Swkoszek NG_QUEUE_LOCK(ngq); 222670700Sjulian } 222771902Sjulian /* 222871902Sjulian * Take us off the work queue if we are there. 2229167677Srwatson * We definately have no work to be done. 223071902Sjulian */ 223171902Sjulian ng_worklist_remove(ngq->q_node); 2232168049Swkoszek NG_QUEUE_UNLOCK(ngq); 223370700Sjulian} 223470700Sjulian 223570700Sjulian/*********************************************************************** 223670700Sjulian* Externally visible method for sending or queueing messages or data. 223770700Sjulian***********************************************************************/ 223870700Sjulian 223970700Sjulian/* 224071849Sjulian * The module code should have filled out the item correctly by this stage: 224170700Sjulian * Common: 224270700Sjulian * reference to destination node. 224370700Sjulian * Reference to destination rcv hook if relevant. 224470700Sjulian * Data: 224570700Sjulian * pointer to mbuf 224670700Sjulian * Control_Message: 224770700Sjulian * pointer to msg. 224870700Sjulian * ID of original sender node. (return address) 224971849Sjulian * Function: 225071849Sjulian * Function pointer 225171849Sjulian * void * argument 225271849Sjulian * integer argument 225370700Sjulian * 225470700Sjulian * The nodes have several routines and macros to help with this task: 225570700Sjulian */ 225670700Sjulian 225770700Sjulianint 2258146281Sglebiusng_snd_item(item_p item, int flags) 225970700Sjulian{ 226071849Sjulian hook_p hook = NGI_HOOK(item); 226171902Sjulian node_p node = NGI_NODE(item); 2262146281Sglebius int queue, rw; 226371902Sjulian struct ng_queue * ngq = &node->nd_input_queue; 2264151256Sglebius int error = 0; 226570700Sjulian 226670784Sjulian#ifdef NETGRAPH_DEBUG 2267152451Sglebius _ngi_check(item, __FILE__, __LINE__); 226870700Sjulian#endif 226970700Sjulian 2270146281Sglebius queue = (flags & NG_QUEUE) ? 1 : 0; 2271146281Sglebius 227270700Sjulian if (item == NULL) { 227371047Sjulian TRAP_ERROR(); 227470700Sjulian return (EINVAL); /* failed to get queue element */ 227570700Sjulian } 227671902Sjulian if (node == NULL) { 227770700Sjulian NG_FREE_ITEM(item); 227871047Sjulian TRAP_ERROR(); 227970700Sjulian return (EINVAL); /* No address */ 228070700Sjulian } 228171047Sjulian switch(item->el_flags & NGQF_TYPE) { 228271047Sjulian case NGQF_DATA: 228369922Sjulian /* 228470700Sjulian * DATA MESSAGE 228570700Sjulian * Delivered to a node via a non-optional hook. 228670700Sjulian * Both should be present in the item even though 228770700Sjulian * the node is derivable from the hook. 228870700Sjulian * References are held on both by the item. 228969922Sjulian */ 2290131112Sjulian 2291131112Sjulian /* Protect nodes from sending NULL pointers 2292131112Sjulian * to each other 2293131112Sjulian */ 2294131123Sjulian if (NGI_M(item) == NULL) 2295131112Sjulian return (EINVAL); 2296131112Sjulian 229770700Sjulian CHECK_DATA_MBUF(NGI_M(item)); 229870700Sjulian if (hook == NULL) { 229970700Sjulian NG_FREE_ITEM(item); 230071047Sjulian TRAP_ERROR(); 230170700Sjulian return(EINVAL); 230270700Sjulian } 230370784Sjulian if ((NG_HOOK_NOT_VALID(hook)) 230470784Sjulian || (NG_NODE_NOT_VALID(NG_HOOK_NODE(hook)))) { 230570700Sjulian NG_FREE_ITEM(item); 230670700Sjulian return (ENOTCONN); 230769922Sjulian } 230870784Sjulian if ((hook->hk_flags & HK_QUEUE)) { 230970700Sjulian queue = 1; 231070700Sjulian } 231171047Sjulian break; 231271047Sjulian case NGQF_MESG: 231370700Sjulian /* 231470700Sjulian * CONTROL MESSAGE 231570700Sjulian * Delivered to a node. 231670700Sjulian * Hook is optional. 231770700Sjulian * References are held by the item on the node and 231870700Sjulian * the hook if it is present. 231970700Sjulian */ 232070784Sjulian if (hook && (hook->hk_flags & HK_QUEUE)) { 232170700Sjulian queue = 1; 232270700Sjulian } 232371047Sjulian break; 232471047Sjulian case NGQF_FN: 232571047Sjulian break; 232671047Sjulian default: 232771047Sjulian NG_FREE_ITEM(item); 232871047Sjulian TRAP_ERROR(); 232971047Sjulian return (EINVAL); 233069922Sjulian } 2331149505Sglebius switch(item->el_flags & NGQF_RW) { 2332149505Sglebius case NGQF_READER: 2333149505Sglebius rw = NGQRW_R; 2334149505Sglebius break; 2335149505Sglebius case NGQF_WRITER: 2336149505Sglebius rw = NGQRW_W; 2337149505Sglebius break; 2338149505Sglebius default: 2339149505Sglebius panic("%s: invalid item flags %lx", __func__, item->el_flags); 2340149505Sglebius } 2341149505Sglebius 234270700Sjulian /* 2343149505Sglebius * If the node specifies single threading, force writer semantics. 2344149505Sglebius * Similarly, the node may say one hook always produces writers. 2345151238Sglebius * These are overrides. 234670700Sjulian */ 2347132464Sjulian if ((node->nd_flags & NGF_FORCE_WRITER) 2348151238Sglebius || (hook && (hook->hk_flags & HK_FORCE_WRITER))) 234970700Sjulian rw = NGQRW_W; 2350149505Sglebius 235170700Sjulian if (queue) { 235270700Sjulian /* Put it on the queue for that node*/ 235370784Sjulian#ifdef NETGRAPH_DEBUG 2354152451Sglebius _ngi_check(item, __FILE__, __LINE__); 235570700Sjulian#endif 2356168049Swkoszek NG_QUEUE_LOCK(ngq); 235770700Sjulian ng_queue_rw(ngq, item, rw); 2358168049Swkoszek NG_QUEUE_UNLOCK(ngq); 2359147774Sglebius 2360147774Sglebius if (flags & NG_PROGRESS) 2361147774Sglebius return (EINPROGRESS); 2362147774Sglebius else 2363147774Sglebius return (0); 236470700Sjulian } 236569922Sjulian 236670700Sjulian /* 236770700Sjulian * We already decided how we will be queueud or treated. 236870700Sjulian * Try get the appropriate operating permission. 236970700Sjulian */ 2370151256Sglebius if (rw == NGQRW_R) 237170700Sjulian item = ng_acquire_read(ngq, item); 2372151256Sglebius else 237370700Sjulian item = ng_acquire_write(ngq, item); 237452419Sjulian 2375151973Sglebius 237670700Sjulian if (item == NULL) { 2377147774Sglebius if (flags & NG_PROGRESS) 2378147774Sglebius return (EINPROGRESS); 2379147774Sglebius else 2380147774Sglebius return (0); 238170700Sjulian } 238252419Sjulian 238370784Sjulian#ifdef NETGRAPH_DEBUG 2384152451Sglebius _ngi_check(item, __FILE__, __LINE__); 238570700Sjulian#endif 2386151973Sglebius 238774078Sjulian NGI_GET_NODE(item, node); /* zaps stored node */ 238852419Sjulian 2389170180Sglebius error = ng_apply_item(node, item, rw); /* drops r/w lock when done */ 239074078Sjulian 239152419Sjulian /* 2392152451Sglebius * If the node goes away when we remove the reference, 239382058Sbrian * whatever we just did caused it.. whatever we do, DO NOT 239474078Sjulian * access the node again! 239574078Sjulian */ 239674078Sjulian if (NG_NODE_UNREF(node) == 0) { 239774078Sjulian return (error); 239874078Sjulian } 239974078Sjulian 2400168049Swkoszek NG_QUEUE_LOCK(ngq); 2401151973Sglebius if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2402148236Sglebius ng_setisr(ngq->q_node); 2403168049Swkoszek NG_QUEUE_UNLOCK(ngq); 240470700Sjulian 240571849Sjulian return (error); 240652419Sjulian} 240752419Sjulian 240852419Sjulian/* 240970700Sjulian * We have an item that was possibly queued somewhere. 241070700Sjulian * It should contain all the information needed 241170700Sjulian * to run it on the appropriate node/hook. 241252419Sjulian */ 2413170180Sglebiusstatic int 2414151238Sglebiusng_apply_item(node_p node, item_p item, int rw) 241552419Sjulian{ 241670700Sjulian hook_p hook; 2417167402Sjulian int error = 0; 241870700Sjulian ng_rcvdata_t *rcvdata; 241971885Sjulian ng_rcvmsg_t *rcvmsg; 2420147774Sglebius ng_apply_t *apply = NULL; 2421147774Sglebius void *context = NULL; 242252419Sjulian 242371849Sjulian NGI_GET_HOOK(item, hook); /* clears stored hook */ 242470784Sjulian#ifdef NETGRAPH_DEBUG 2425152451Sglebius _ngi_check(item, __FILE__, __LINE__); 242670700Sjulian#endif 2427147774Sglebius 2428147774Sglebius /* 2429151973Sglebius * If the item has an "apply" callback, store it. 2430151973Sglebius * Clear item's callback immediately, to avoid an extra call if 2431151973Sglebius * the item is reused by the destination node. 2432147774Sglebius */ 2433147774Sglebius if (item->apply != NULL) { 2434147774Sglebius apply = item->apply; 2435147774Sglebius context = item->context; 2436147774Sglebius item->apply = NULL; 2437147774Sglebius } 2438147774Sglebius 243971047Sjulian switch (item->el_flags & NGQF_TYPE) { 244070700Sjulian case NGQF_DATA: 244170700Sjulian /* 244270700Sjulian * Check things are still ok as when we were queued. 244370700Sjulian */ 244470700Sjulian if ((hook == NULL) 244570784Sjulian || NG_HOOK_NOT_VALID(hook) 244671885Sjulian || NG_NODE_NOT_VALID(node) ) { 2447167402Sjulian error = EIO; 244870700Sjulian NG_FREE_ITEM(item); 244971885Sjulian break; 245070700Sjulian } 245171885Sjulian /* 245271885Sjulian * If no receive method, just silently drop it. 245371885Sjulian * Give preference to the hook over-ride method 245471885Sjulian */ 2455152451Sglebius if ((!(rcvdata = hook->hk_rcvdata)) 245671885Sjulian && (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) { 245771885Sjulian error = 0; 245871885Sjulian NG_FREE_ITEM(item); 245971885Sjulian break; 246071885Sjulian } 2461167402Sjulian error = (*rcvdata)(hook, item); 246270700Sjulian break; 246370700Sjulian case NGQF_MESG: 246470700Sjulian if (hook) { 246570784Sjulian if (NG_HOOK_NOT_VALID(hook)) { 246671849Sjulian /* 246771849Sjulian * The hook has been zapped then we can't 2468167677Srwatson * use it. Immediately drop its reference. 246971849Sjulian * The message may not need it. 247071849Sjulian */ 247170784Sjulian NG_HOOK_UNREF(hook); 247270700Sjulian hook = NULL; 247370700Sjulian } 247470700Sjulian } 247570700Sjulian /* 247670700Sjulian * Similarly, if the node is a zombie there is 247770700Sjulian * nothing we can do with it, drop everything. 247870700Sjulian */ 247970784Sjulian if (NG_NODE_NOT_VALID(node)) { 248071047Sjulian TRAP_ERROR(); 2481167402Sjulian error = EINVAL; 248270700Sjulian NG_FREE_ITEM(item); 248370700Sjulian } else { 248470700Sjulian /* 248570700Sjulian * Call the appropriate message handler for the object. 248670700Sjulian * It is up to the message handler to free the message. 248770700Sjulian * If it's a generic message, handle it generically, 248870700Sjulian * otherwise call the type's message handler 248970700Sjulian * (if it exists) 249070700Sjulian * XXX (race). Remember that a queued message may 249170700Sjulian * reference a node or hook that has just been 249270700Sjulian * invalidated. It will exist as the queue code 249370700Sjulian * is holding a reference, but.. 249470700Sjulian */ 249570700Sjulian 249670700Sjulian struct ng_mesg *msg = NGI_MSG(item); 249770700Sjulian 2498152451Sglebius /* 249971885Sjulian * check if the generic handler owns it. 250071885Sjulian */ 250170700Sjulian if ((msg->header.typecookie == NGM_GENERIC_COOKIE) 250270700Sjulian && ((msg->header.flags & NGF_RESP) == 0)) { 2503167402Sjulian error = ng_generic_msg(node, item, hook); 250471885Sjulian break; 250570700Sjulian } 250671885Sjulian /* 250771885Sjulian * Now see if there is a handler (hook or node specific) 250871885Sjulian * in the target node. If none, silently discard. 250971885Sjulian */ 251071885Sjulian if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) 251171885Sjulian && (!(rcvmsg = node->nd_type->rcvmsg))) { 251271885Sjulian TRAP_ERROR(); 2513167402Sjulian error = 0; 251471885Sjulian NG_FREE_ITEM(item); 251571885Sjulian break; 251671885Sjulian } 2517167402Sjulian error = (*rcvmsg)(node, item, hook); 251870700Sjulian } 251970700Sjulian break; 252071047Sjulian case NGQF_FN: 252171047Sjulian /* 252271047Sjulian * We have to implicitly trust the hook, 252371047Sjulian * as some of these are used for system purposes 252471849Sjulian * where the hook is invalid. In the case of 252571849Sjulian * the shutdown message we allow it to hit 252671849Sjulian * even if the node is invalid. 252771047Sjulian */ 2528152451Sglebius if ((NG_NODE_NOT_VALID(node)) 252971849Sjulian && (NGI_FN(item) != &ng_rmnode)) { 253071047Sjulian TRAP_ERROR(); 2531167402Sjulian error = EINVAL; 2532143384Sglebius NG_FREE_ITEM(item); 253371047Sjulian break; 253471047Sjulian } 253571849Sjulian (*NGI_FN(item))(node, hook, NGI_ARG1(item), NGI_ARG2(item)); 253671047Sjulian NG_FREE_ITEM(item); 253771047Sjulian break; 253871047Sjulian 253970700Sjulian } 254070700Sjulian /* 254170700Sjulian * We held references on some of the resources 254270700Sjulian * that we took from the item. Now that we have 254370700Sjulian * finished doing everything, drop those references. 254470700Sjulian */ 254570700Sjulian if (hook) { 254670784Sjulian NG_HOOK_UNREF(hook); 254770700Sjulian } 254870700Sjulian 2549151238Sglebius if (rw == NGQRW_R) { 255070784Sjulian ng_leave_read(&node->nd_input_queue); 2551167402Sjulian } else { 255270784Sjulian ng_leave_write(&node->nd_input_queue); 2553167402Sjulian } 2554147774Sglebius 2555147774Sglebius /* Apply callback. */ 2556147774Sglebius if (apply != NULL) 2557147774Sglebius (*apply)(context, error); 2558147774Sglebius 2559170180Sglebius return (error); 256070700Sjulian} 256170700Sjulian 256270700Sjulian/*********************************************************************** 256370700Sjulian * Implement the 'generic' control messages 256470700Sjulian ***********************************************************************/ 256570700Sjulianstatic int 256670700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook) 256770700Sjulian{ 256870700Sjulian int error = 0; 256970700Sjulian struct ng_mesg *msg; 257070700Sjulian struct ng_mesg *resp = NULL; 257170700Sjulian 257270700Sjulian NGI_GET_MSG(item, msg); 257352419Sjulian if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 257471047Sjulian TRAP_ERROR(); 257570700Sjulian error = EINVAL; 257670700Sjulian goto out; 257752419Sjulian } 257852419Sjulian switch (msg->header.cmd) { 257952419Sjulian case NGM_SHUTDOWN: 258071849Sjulian ng_rmnode(here, NULL, NULL, 0); 258152419Sjulian break; 258252419Sjulian case NGM_MKPEER: 258352419Sjulian { 258452419Sjulian struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 258552419Sjulian 258652419Sjulian if (msg->header.arglen != sizeof(*mkp)) { 258771047Sjulian TRAP_ERROR(); 258870700Sjulian error = EINVAL; 258970700Sjulian break; 259052419Sjulian } 259152419Sjulian mkp->type[sizeof(mkp->type) - 1] = '\0'; 259252419Sjulian mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 259352419Sjulian mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 259452419Sjulian error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 259552419Sjulian break; 259652419Sjulian } 259752419Sjulian case NGM_CONNECT: 259852419Sjulian { 259952419Sjulian struct ngm_connect *const con = 260052419Sjulian (struct ngm_connect *) msg->data; 260152419Sjulian node_p node2; 260252419Sjulian 260352419Sjulian if (msg->header.arglen != sizeof(*con)) { 260471047Sjulian TRAP_ERROR(); 260570700Sjulian error = EINVAL; 260670700Sjulian break; 260752419Sjulian } 260852419Sjulian con->path[sizeof(con->path) - 1] = '\0'; 260952419Sjulian con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 261052419Sjulian con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 261170700Sjulian /* Don't forget we get a reference.. */ 261270700Sjulian error = ng_path2noderef(here, con->path, &node2, NULL); 261352419Sjulian if (error) 261452419Sjulian break; 261552419Sjulian error = ng_con_nodes(here, con->ourhook, node2, con->peerhook); 261670784Sjulian NG_NODE_UNREF(node2); 261752419Sjulian break; 261852419Sjulian } 261952419Sjulian case NGM_NAME: 262052419Sjulian { 262152419Sjulian struct ngm_name *const nam = (struct ngm_name *) msg->data; 262252419Sjulian 262352419Sjulian if (msg->header.arglen != sizeof(*nam)) { 262471047Sjulian TRAP_ERROR(); 262570700Sjulian error = EINVAL; 262670700Sjulian break; 262752419Sjulian } 262852419Sjulian nam->name[sizeof(nam->name) - 1] = '\0'; 262952419Sjulian error = ng_name_node(here, nam->name); 263052419Sjulian break; 263152419Sjulian } 263252419Sjulian case NGM_RMHOOK: 263352419Sjulian { 263452419Sjulian struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 263552419Sjulian hook_p hook; 263652419Sjulian 263752419Sjulian if (msg->header.arglen != sizeof(*rmh)) { 263871047Sjulian TRAP_ERROR(); 263970700Sjulian error = EINVAL; 264070700Sjulian break; 264152419Sjulian } 264252419Sjulian rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 264354096Sarchie if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 264452419Sjulian ng_destroy_hook(hook); 264552419Sjulian break; 264652419Sjulian } 264752419Sjulian case NGM_NODEINFO: 264852419Sjulian { 264952419Sjulian struct nodeinfo *ni; 265052419Sjulian 265170700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT); 265252419Sjulian if (resp == NULL) { 265352419Sjulian error = ENOMEM; 265452419Sjulian break; 265552419Sjulian } 265652419Sjulian 265752419Sjulian /* Fill in node info */ 265870700Sjulian ni = (struct nodeinfo *) resp->data; 265970784Sjulian if (NG_NODE_HAS_NAME(here)) 2660125028Sharti strcpy(ni->name, NG_NODE_NAME(here)); 2661125028Sharti strcpy(ni->type, here->nd_type->name); 266252722Sjulian ni->id = ng_node2ID(here); 266370784Sjulian ni->hooks = here->nd_numhooks; 266452419Sjulian break; 266552419Sjulian } 266652419Sjulian case NGM_LISTHOOKS: 266752419Sjulian { 266870784Sjulian const int nhooks = here->nd_numhooks; 266952419Sjulian struct hooklist *hl; 267052419Sjulian struct nodeinfo *ni; 267152419Sjulian hook_p hook; 267252419Sjulian 267352419Sjulian /* Get response struct */ 267470700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*hl) 267570700Sjulian + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 267652419Sjulian if (resp == NULL) { 267752419Sjulian error = ENOMEM; 267852419Sjulian break; 267952419Sjulian } 268070700Sjulian hl = (struct hooklist *) resp->data; 268152419Sjulian ni = &hl->nodeinfo; 268252419Sjulian 268352419Sjulian /* Fill in node info */ 268470784Sjulian if (NG_NODE_HAS_NAME(here)) 2685125028Sharti strcpy(ni->name, NG_NODE_NAME(here)); 2686125028Sharti strcpy(ni->type, here->nd_type->name); 268752722Sjulian ni->id = ng_node2ID(here); 268852419Sjulian 268952419Sjulian /* Cycle through the linked list of hooks */ 269052419Sjulian ni->hooks = 0; 269170784Sjulian LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) { 269252419Sjulian struct linkinfo *const link = &hl->link[ni->hooks]; 269352419Sjulian 269452419Sjulian if (ni->hooks >= nhooks) { 269552419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 269687599Sobrien __func__, "hooks"); 269752419Sjulian break; 269852419Sjulian } 269970784Sjulian if (NG_HOOK_NOT_VALID(hook)) 270052419Sjulian continue; 2701125028Sharti strcpy(link->ourhook, NG_HOOK_NAME(hook)); 2702125028Sharti strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook)); 270370784Sjulian if (NG_PEER_NODE_NAME(hook)[0] != '\0') 2704125028Sharti strcpy(link->nodeinfo.name, 2705125028Sharti NG_PEER_NODE_NAME(hook)); 2706125028Sharti strcpy(link->nodeinfo.type, 2707125028Sharti NG_PEER_NODE(hook)->nd_type->name); 270870784Sjulian link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook)); 270970784Sjulian link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks; 271052419Sjulian ni->hooks++; 271152419Sjulian } 271252419Sjulian break; 271352419Sjulian } 271452419Sjulian 271552419Sjulian case NGM_LISTNAMES: 271652419Sjulian case NGM_LISTNODES: 271752419Sjulian { 271852419Sjulian const int unnamed = (msg->header.cmd == NGM_LISTNODES); 271952419Sjulian struct namelist *nl; 272052419Sjulian node_p node; 272152419Sjulian int num = 0; 272252419Sjulian 272372200Sbmilekic mtx_lock(&ng_nodelist_mtx); 272452419Sjulian /* Count number of nodes */ 272570784Sjulian LIST_FOREACH(node, &ng_nodelist, nd_nodes) { 272670912Sjulian if (NG_NODE_IS_VALID(node) 272770912Sjulian && (unnamed || NG_NODE_HAS_NAME(node))) { 272852419Sjulian num++; 272970912Sjulian } 273052419Sjulian } 273172200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 273252419Sjulian 273352419Sjulian /* Get response struct */ 273470700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*nl) 273570700Sjulian + (num * sizeof(struct nodeinfo)), M_NOWAIT); 273652419Sjulian if (resp == NULL) { 273752419Sjulian error = ENOMEM; 273852419Sjulian break; 273952419Sjulian } 274070700Sjulian nl = (struct namelist *) resp->data; 274152419Sjulian 274252419Sjulian /* Cycle through the linked list of nodes */ 274352419Sjulian nl->numnames = 0; 274472200Sbmilekic mtx_lock(&ng_nodelist_mtx); 274570784Sjulian LIST_FOREACH(node, &ng_nodelist, nd_nodes) { 274652419Sjulian struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; 274752419Sjulian 2748159373Sglebius if (NG_NODE_NOT_VALID(node)) 2749159373Sglebius continue; 2750159373Sglebius if (!unnamed && (! NG_NODE_HAS_NAME(node))) 2751159373Sglebius continue; 275252419Sjulian if (nl->numnames >= num) { 275352419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 275487599Sobrien __func__, "nodes"); 275552419Sjulian break; 275652419Sjulian } 275770784Sjulian if (NG_NODE_HAS_NAME(node)) 2758125028Sharti strcpy(np->name, NG_NODE_NAME(node)); 2759125028Sharti strcpy(np->type, node->nd_type->name); 276052722Sjulian np->id = ng_node2ID(node); 276170784Sjulian np->hooks = node->nd_numhooks; 276252419Sjulian nl->numnames++; 276352419Sjulian } 276472200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 276552419Sjulian break; 276652419Sjulian } 276752419Sjulian 276852419Sjulian case NGM_LISTTYPES: 276952419Sjulian { 277052419Sjulian struct typelist *tl; 277152419Sjulian struct ng_type *type; 277252419Sjulian int num = 0; 277352419Sjulian 277472200Sbmilekic mtx_lock(&ng_typelist_mtx); 277552419Sjulian /* Count number of types */ 277670912Sjulian LIST_FOREACH(type, &ng_typelist, types) { 277752419Sjulian num++; 277870912Sjulian } 277972200Sbmilekic mtx_unlock(&ng_typelist_mtx); 278052419Sjulian 278152419Sjulian /* Get response struct */ 278270700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*tl) 278370700Sjulian + (num * sizeof(struct typeinfo)), M_NOWAIT); 278452419Sjulian if (resp == NULL) { 278552419Sjulian error = ENOMEM; 278652419Sjulian break; 278752419Sjulian } 278870700Sjulian tl = (struct typelist *) resp->data; 278952419Sjulian 279052419Sjulian /* Cycle through the linked list of types */ 279152419Sjulian tl->numtypes = 0; 279272200Sbmilekic mtx_lock(&ng_typelist_mtx); 279370700Sjulian LIST_FOREACH(type, &ng_typelist, types) { 279452419Sjulian struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 279552419Sjulian 279652419Sjulian if (tl->numtypes >= num) { 279752419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 279887599Sobrien __func__, "types"); 279952419Sjulian break; 280052419Sjulian } 2801125028Sharti strcpy(tp->type_name, type->name); 280271603Sjulian tp->numnodes = type->refs - 1; /* don't count list */ 280352419Sjulian tl->numtypes++; 280452419Sjulian } 280572200Sbmilekic mtx_unlock(&ng_typelist_mtx); 280652419Sjulian break; 280752419Sjulian } 280852419Sjulian 280953913Sarchie case NGM_BINARY2ASCII: 281053913Sarchie { 281164510Sarchie int bufSize = 20 * 1024; /* XXX hard coded constant */ 281253913Sarchie const struct ng_parse_type *argstype; 281353913Sarchie const struct ng_cmdlist *c; 281470700Sjulian struct ng_mesg *binary, *ascii; 281553913Sarchie 281653913Sarchie /* Data area must contain a valid netgraph message */ 281753913Sarchie binary = (struct ng_mesg *)msg->data; 2818152451Sglebius if (msg->header.arglen < sizeof(struct ng_mesg) || 2819152451Sglebius (msg->header.arglen - sizeof(struct ng_mesg) < 2820152451Sglebius binary->header.arglen)) { 282171047Sjulian TRAP_ERROR(); 282253913Sarchie error = EINVAL; 282353913Sarchie break; 282453913Sarchie } 282553913Sarchie 282670700Sjulian /* Get a response message with lots of room */ 282770700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 282870159Sjulian if (resp == NULL) { 282953913Sarchie error = ENOMEM; 283053913Sarchie break; 283153913Sarchie } 283270700Sjulian ascii = (struct ng_mesg *)resp->data; 283353913Sarchie 283453913Sarchie /* Copy binary message header to response message payload */ 283553913Sarchie bcopy(binary, ascii, sizeof(*binary)); 283653913Sarchie 283753913Sarchie /* Find command by matching typecookie and command number */ 283870784Sjulian for (c = here->nd_type->cmdlist; 283953913Sarchie c != NULL && c->name != NULL; c++) { 284053913Sarchie if (binary->header.typecookie == c->cookie 284153913Sarchie && binary->header.cmd == c->cmd) 284253913Sarchie break; 284353913Sarchie } 284453913Sarchie if (c == NULL || c->name == NULL) { 284553913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 284653913Sarchie if (binary->header.typecookie == c->cookie 284753913Sarchie && binary->header.cmd == c->cmd) 284853913Sarchie break; 284953913Sarchie } 285053913Sarchie if (c->name == NULL) { 285170700Sjulian NG_FREE_MSG(resp); 285253913Sarchie error = ENOSYS; 285353913Sarchie break; 285453913Sarchie } 285553913Sarchie } 285653913Sarchie 285753913Sarchie /* Convert command name to ASCII */ 285853913Sarchie snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 285953913Sarchie "%s", c->name); 286053913Sarchie 286153913Sarchie /* Convert command arguments to ASCII */ 286253913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 286353913Sarchie c->respType : c->mesgType; 286470912Sjulian if (argstype == NULL) { 286553913Sarchie *ascii->data = '\0'; 286670912Sjulian } else { 286753913Sarchie if ((error = ng_unparse(argstype, 286853913Sarchie (u_char *)binary->data, 286953913Sarchie ascii->data, bufSize)) != 0) { 287070700Sjulian NG_FREE_MSG(resp); 287153913Sarchie break; 287253913Sarchie } 287353913Sarchie } 287453913Sarchie 287553913Sarchie /* Return the result as struct ng_mesg plus ASCII string */ 287653913Sarchie bufSize = strlen(ascii->data) + 1; 287753913Sarchie ascii->header.arglen = bufSize; 287870700Sjulian resp->header.arglen = sizeof(*ascii) + bufSize; 287953913Sarchie break; 288053913Sarchie } 288153913Sarchie 288253913Sarchie case NGM_ASCII2BINARY: 288353913Sarchie { 288453913Sarchie int bufSize = 2000; /* XXX hard coded constant */ 288553913Sarchie const struct ng_cmdlist *c; 288653913Sarchie const struct ng_parse_type *argstype; 288770700Sjulian struct ng_mesg *ascii, *binary; 288859178Sarchie int off = 0; 288953913Sarchie 289053913Sarchie /* Data area must contain at least a struct ng_mesg + '\0' */ 289153913Sarchie ascii = (struct ng_mesg *)msg->data; 2892152451Sglebius if ((msg->header.arglen < sizeof(*ascii) + 1) || 2893152451Sglebius (ascii->header.arglen < 1) || 2894152451Sglebius (msg->header.arglen < sizeof(*ascii) + 2895152451Sglebius ascii->header.arglen)) { 289671047Sjulian TRAP_ERROR(); 289753913Sarchie error = EINVAL; 289853913Sarchie break; 289953913Sarchie } 290053913Sarchie ascii->data[ascii->header.arglen - 1] = '\0'; 290153913Sarchie 290270700Sjulian /* Get a response message with lots of room */ 290370700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 290470159Sjulian if (resp == NULL) { 290553913Sarchie error = ENOMEM; 290653913Sarchie break; 290753913Sarchie } 290870700Sjulian binary = (struct ng_mesg *)resp->data; 290953913Sarchie 291053913Sarchie /* Copy ASCII message header to response message payload */ 291153913Sarchie bcopy(ascii, binary, sizeof(*ascii)); 291253913Sarchie 291353913Sarchie /* Find command by matching ASCII command string */ 291470784Sjulian for (c = here->nd_type->cmdlist; 291553913Sarchie c != NULL && c->name != NULL; c++) { 291653913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 291753913Sarchie break; 291853913Sarchie } 291953913Sarchie if (c == NULL || c->name == NULL) { 292053913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 292153913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 292253913Sarchie break; 292353913Sarchie } 292453913Sarchie if (c->name == NULL) { 292570700Sjulian NG_FREE_MSG(resp); 292653913Sarchie error = ENOSYS; 292753913Sarchie break; 292853913Sarchie } 292953913Sarchie } 293053913Sarchie 293153913Sarchie /* Convert command name to binary */ 293253913Sarchie binary->header.cmd = c->cmd; 293353913Sarchie binary->header.typecookie = c->cookie; 293453913Sarchie 293553913Sarchie /* Convert command arguments to binary */ 293653913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 293753913Sarchie c->respType : c->mesgType; 293870912Sjulian if (argstype == NULL) { 293953913Sarchie bufSize = 0; 294070912Sjulian } else { 294153913Sarchie if ((error = ng_parse(argstype, ascii->data, 294253913Sarchie &off, (u_char *)binary->data, &bufSize)) != 0) { 294370700Sjulian NG_FREE_MSG(resp); 294453913Sarchie break; 294553913Sarchie } 294653913Sarchie } 294753913Sarchie 294853913Sarchie /* Return the result */ 294953913Sarchie binary->header.arglen = bufSize; 295070700Sjulian resp->header.arglen = sizeof(*binary) + bufSize; 295153913Sarchie break; 295253913Sarchie } 295353913Sarchie 295462471Sphk case NGM_TEXT_CONFIG: 295552419Sjulian case NGM_TEXT_STATUS: 295652419Sjulian /* 295752419Sjulian * This one is tricky as it passes the command down to the 295852419Sjulian * actual node, even though it is a generic type command. 295970700Sjulian * This means we must assume that the item/msg is already freed 296052419Sjulian * when control passes back to us. 296152419Sjulian */ 296270784Sjulian if (here->nd_type->rcvmsg != NULL) { 296370700Sjulian NGI_MSG(item) = msg; /* put it back as we found it */ 296470784Sjulian return((*here->nd_type->rcvmsg)(here, item, lasthook)); 296552419Sjulian } 296652419Sjulian /* Fall through if rcvmsg not supported */ 296752419Sjulian default: 296871047Sjulian TRAP_ERROR(); 296952419Sjulian error = EINVAL; 297052419Sjulian } 297170700Sjulian /* 297270700Sjulian * Sometimes a generic message may be statically allocated 297370700Sjulian * to avoid problems with allocating when in tight memeory situations. 297470700Sjulian * Don't free it if it is so. 297570700Sjulian * I break them appart here, because erros may cause a free if the item 297670700Sjulian * in which case we'd be doing it twice. 297770700Sjulian * they are kept together above, to simplify freeing. 297870700Sjulian */ 297970700Sjulianout: 298070700Sjulian NG_RESPOND_MSG(error, here, item, resp); 298171849Sjulian if (msg) 298270700Sjulian NG_FREE_MSG(msg); 298352419Sjulian return (error); 298452419Sjulian} 298552419Sjulian 298652419Sjulian/************************************************************************ 2987146213Sglebius Queue element get/free routines 2988146213Sglebius************************************************************************/ 2989146213Sglebius 2990146213Sglebiusuma_zone_t ng_qzone; 2991146213Sglebiusstatic int maxalloc = 512; /* limit the damage of a leak */ 2992146213Sglebius 2993146213SglebiusTUNABLE_INT("net.graph.maxalloc", &maxalloc); 2994146213SglebiusSYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc, 2995146213Sglebius 0, "Maximum number of queue items to allocate"); 2996146213Sglebius 2997146213Sglebius#ifdef NETGRAPH_DEBUG 2998146213Sglebiusstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist); 2999146213Sglebiusstatic int allocated; /* number of items malloc'd */ 3000146213Sglebius#endif 3001146213Sglebius 3002146213Sglebius/* 3003146213Sglebius * Get a queue entry. 3004146213Sglebius * This is usually called when a packet first enters netgraph. 3005146213Sglebius * By definition, this is usually from an interrupt, or from a user. 3006146213Sglebius * Users are not so important, but try be quick for the times that it's 3007146213Sglebius * an interrupt. 3008146213Sglebius */ 3009146213Sglebiusstatic __inline item_p 3010146281Sglebiusng_getqblk(int flags) 3011146213Sglebius{ 3012146213Sglebius item_p item = NULL; 3013146281Sglebius int wait; 3014146213Sglebius 3015146281Sglebius wait = (flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT; 3016146213Sglebius 3017146281Sglebius item = uma_zalloc(ng_qzone, wait | M_ZERO); 3018146281Sglebius 3019146213Sglebius#ifdef NETGRAPH_DEBUG 3020146213Sglebius if (item) { 3021146213Sglebius mtx_lock(&ngq_mtx); 3022146213Sglebius TAILQ_INSERT_TAIL(&ng_itemlist, item, all); 3023146213Sglebius allocated++; 3024146213Sglebius mtx_unlock(&ngq_mtx); 3025146213Sglebius } 3026146213Sglebius#endif 3027146213Sglebius 3028146213Sglebius return (item); 3029146213Sglebius} 3030146213Sglebius 3031146213Sglebius/* 3032146213Sglebius * Release a queue entry 3033146213Sglebius */ 3034146213Sglebiusvoid 3035146213Sglebiusng_free_item(item_p item) 3036146213Sglebius{ 3037151283Sglebius KASSERT(item->apply == NULL, ("%s: leaking apply callback", __func__)); 3038151283Sglebius 3039146213Sglebius /* 3040146213Sglebius * The item may hold resources on it's own. We need to free 3041146213Sglebius * these before we can free the item. What they are depends upon 3042146213Sglebius * what kind of item it is. it is important that nodes zero 3043146213Sglebius * out pointers to resources that they remove from the item 3044146213Sglebius * or we release them again here. 3045146213Sglebius */ 3046146213Sglebius switch (item->el_flags & NGQF_TYPE) { 3047146213Sglebius case NGQF_DATA: 3048146213Sglebius /* If we have an mbuf still attached.. */ 3049146213Sglebius NG_FREE_M(_NGI_M(item)); 3050146213Sglebius break; 3051146213Sglebius case NGQF_MESG: 3052146213Sglebius _NGI_RETADDR(item) = 0; 3053146213Sglebius NG_FREE_MSG(_NGI_MSG(item)); 3054146213Sglebius break; 3055146213Sglebius case NGQF_FN: 3056146213Sglebius /* nothing to free really, */ 3057146213Sglebius _NGI_FN(item) = NULL; 3058146213Sglebius _NGI_ARG1(item) = NULL; 3059146213Sglebius _NGI_ARG2(item) = 0; 3060146213Sglebius case NGQF_UNDEF: 3061146213Sglebius break; 3062146213Sglebius } 3063146213Sglebius /* If we still have a node or hook referenced... */ 3064146213Sglebius _NGI_CLR_NODE(item); 3065146213Sglebius _NGI_CLR_HOOK(item); 3066146213Sglebius 3067146213Sglebius#ifdef NETGRAPH_DEBUG 3068146213Sglebius mtx_lock(&ngq_mtx); 3069146213Sglebius TAILQ_REMOVE(&ng_itemlist, item, all); 3070146213Sglebius allocated--; 3071146213Sglebius mtx_unlock(&ngq_mtx); 3072146213Sglebius#endif 3073146213Sglebius uma_zfree(ng_qzone, item); 3074146213Sglebius} 3075146213Sglebius 3076146213Sglebius/************************************************************************ 307752419Sjulian Module routines 307852419Sjulian************************************************************************/ 307952419Sjulian 308052419Sjulian/* 308152419Sjulian * Handle the loading/unloading of a netgraph node type module 308252419Sjulian */ 308352419Sjulianint 308452419Sjulianng_mod_event(module_t mod, int event, void *data) 308552419Sjulian{ 308652419Sjulian struct ng_type *const type = data; 308752419Sjulian int s, error = 0; 308852419Sjulian 308952419Sjulian switch (event) { 309052419Sjulian case MOD_LOAD: 309152419Sjulian 309252419Sjulian /* Register new netgraph node type */ 309352419Sjulian s = splnet(); 309452419Sjulian if ((error = ng_newtype(type)) != 0) { 309552419Sjulian splx(s); 309652419Sjulian break; 309752419Sjulian } 309852419Sjulian 309952419Sjulian /* Call type specific code */ 310052419Sjulian if (type->mod_event != NULL) 310170700Sjulian if ((error = (*type->mod_event)(mod, event, data))) { 310272200Sbmilekic mtx_lock(&ng_typelist_mtx); 310371603Sjulian type->refs--; /* undo it */ 310452419Sjulian LIST_REMOVE(type, types); 310572200Sbmilekic mtx_unlock(&ng_typelist_mtx); 310670700Sjulian } 310752419Sjulian splx(s); 310852419Sjulian break; 310952419Sjulian 311052419Sjulian case MOD_UNLOAD: 311152419Sjulian s = splnet(); 311271603Sjulian if (type->refs > 1) { /* make sure no nodes exist! */ 311352419Sjulian error = EBUSY; 311471603Sjulian } else { 311571603Sjulian if (type->refs == 0) { 311671603Sjulian /* failed load, nothing to undo */ 311771603Sjulian splx(s); 311871603Sjulian break; 311971603Sjulian } 312052419Sjulian if (type->mod_event != NULL) { /* check with type */ 312152419Sjulian error = (*type->mod_event)(mod, event, data); 312252419Sjulian if (error != 0) { /* type refuses.. */ 312352419Sjulian splx(s); 312452419Sjulian break; 312552419Sjulian } 312652419Sjulian } 312772200Sbmilekic mtx_lock(&ng_typelist_mtx); 312852419Sjulian LIST_REMOVE(type, types); 312972200Sbmilekic mtx_unlock(&ng_typelist_mtx); 313052419Sjulian } 313152419Sjulian splx(s); 313252419Sjulian break; 313352419Sjulian 313452419Sjulian default: 313552419Sjulian if (type->mod_event != NULL) 313652419Sjulian error = (*type->mod_event)(mod, event, data); 313752419Sjulian else 3138132199Sphk error = EOPNOTSUPP; /* XXX ? */ 313952419Sjulian break; 314052419Sjulian } 314152419Sjulian return (error); 314252419Sjulian} 314352419Sjulian 314452419Sjulian/* 314552419Sjulian * Handle loading and unloading for this code. 314652419Sjulian * The only thing we need to link into is the NETISR strucure. 314752419Sjulian */ 314852419Sjulianstatic int 314952419Sjulianngb_mod_event(module_t mod, int event, void *data) 315052419Sjulian{ 3151146212Sglebius int error = 0; 315252419Sjulian 315352419Sjulian switch (event) { 315452419Sjulian case MOD_LOAD: 3155146212Sglebius /* Initialize everything. */ 3156168049Swkoszek NG_WORKLIST_LOCK_INIT(); 3157123278Struckman mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL, 3158123278Struckman MTX_DEF); 3159123278Struckman mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL, 3160123278Struckman MTX_DEF); 3161123278Struckman mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL, 3162123278Struckman MTX_DEF); 3163151974Sglebius mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL, 3164151974Sglebius MTX_DEF); 3165146212Sglebius#ifdef NETGRAPH_DEBUG 3166146212Sglebius mtx_init(&ngq_mtx, "netgraph item list mutex", NULL, 3167123278Struckman MTX_DEF); 3168146212Sglebius#endif 3169146212Sglebius ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item), 3170146212Sglebius NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0); 3171146212Sglebius uma_zone_set_max(ng_qzone, maxalloc); 3172141719Sglebius netisr_register(NETISR_NETGRAPH, (netisr_t *)ngintr, NULL, 3173141719Sglebius NETISR_MPSAFE); 317452419Sjulian break; 317552419Sjulian case MOD_UNLOAD: 3176167677Srwatson /* You can't unload it because an interface may be using it. */ 317752419Sjulian error = EBUSY; 317852419Sjulian break; 317952419Sjulian default: 318052419Sjulian error = EOPNOTSUPP; 318152419Sjulian break; 318252419Sjulian } 318352419Sjulian return (error); 318452419Sjulian} 318552419Sjulian 318652419Sjulianstatic moduledata_t netgraph_mod = { 318752419Sjulian "netgraph", 318852419Sjulian ngb_mod_event, 318952419Sjulian (NULL) 319052419Sjulian}; 3191139774SemaxDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE); 319272946SjulianSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family"); 319372946SjulianSYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,""); 319472946SjulianSYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, ""); 319552419Sjulian 319670784Sjulian#ifdef NETGRAPH_DEBUG 319770700Sjulianvoid 319870784Sjuliandumphook (hook_p hook, char *file, int line) 319970784Sjulian{ 320070784Sjulian printf("hook: name %s, %d refs, Last touched:\n", 320170784Sjulian _NG_HOOK_NAME(hook), hook->hk_refs); 320270784Sjulian printf(" Last active @ %s, line %d\n", 320370784Sjulian hook->lastfile, hook->lastline); 320470784Sjulian if (line) { 320570784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 320670784Sjulian } 320770784Sjulian} 320870784Sjulian 320970784Sjulianvoid 321070784Sjuliandumpnode(node_p node, char *file, int line) 321170784Sjulian{ 321270784Sjulian printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n", 321371047Sjulian _NG_NODE_ID(node), node->nd_type->name, 321470784Sjulian node->nd_numhooks, node->nd_flags, 321570784Sjulian node->nd_refs, node->nd_name); 321670784Sjulian printf(" Last active @ %s, line %d\n", 321770784Sjulian node->lastfile, node->lastline); 321870784Sjulian if (line) { 321970784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 322070784Sjulian } 322170784Sjulian} 322270784Sjulian 322370784Sjulianvoid 322470700Sjuliandumpitem(item_p item, char *file, int line) 322570700Sjulian{ 3226146212Sglebius printf(" ACTIVE item, last used at %s, line %d", 3227146212Sglebius item->lastfile, item->lastline); 3228146212Sglebius switch(item->el_flags & NGQF_TYPE) { 3229146212Sglebius case NGQF_DATA: 3230146212Sglebius printf(" - [data]\n"); 3231146212Sglebius break; 3232146212Sglebius case NGQF_MESG: 3233146212Sglebius printf(" - retaddr[%d]:\n", _NGI_RETADDR(item)); 3234146212Sglebius break; 3235146212Sglebius case NGQF_FN: 3236146212Sglebius printf(" - fn@%p (%p, %p, %p, %d (%x))\n", 3237146212Sglebius item->body.fn.fn_fn, 3238149735Sglebius _NGI_NODE(item), 3239149735Sglebius _NGI_HOOK(item), 3240146212Sglebius item->body.fn.fn_arg1, 3241146212Sglebius item->body.fn.fn_arg2, 3242146212Sglebius item->body.fn.fn_arg2); 3243146212Sglebius break; 3244146212Sglebius case NGQF_UNDEF: 3245146212Sglebius printf(" - UNDEFINED!\n"); 324652419Sjulian } 324770784Sjulian if (line) { 324870784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 3249149735Sglebius if (_NGI_NODE(item)) { 325070784Sjulian printf("node %p ([%x])\n", 3251149735Sglebius _NGI_NODE(item), ng_node2ID(_NGI_NODE(item))); 325270784Sjulian } 325370784Sjulian } 325470700Sjulian} 325552419Sjulian 325670784Sjulianstatic void 325770784Sjulianng_dumpitems(void) 325870784Sjulian{ 325970784Sjulian item_p item; 326070784Sjulian int i = 1; 326170784Sjulian TAILQ_FOREACH(item, &ng_itemlist, all) { 326270784Sjulian printf("[%d] ", i++); 326370784Sjulian dumpitem(item, NULL, 0); 326470784Sjulian } 326570784Sjulian} 326670784Sjulian 326770784Sjulianstatic void 326870784Sjulianng_dumpnodes(void) 326970784Sjulian{ 327070784Sjulian node_p node; 327170784Sjulian int i = 1; 3272131008Srwatson mtx_lock(&ng_nodelist_mtx); 327370784Sjulian SLIST_FOREACH(node, &ng_allnodes, nd_all) { 327470784Sjulian printf("[%d] ", i++); 327570784Sjulian dumpnode(node, NULL, 0); 327670784Sjulian } 3277131008Srwatson mtx_unlock(&ng_nodelist_mtx); 327870784Sjulian} 327970784Sjulian 328070784Sjulianstatic void 328170784Sjulianng_dumphooks(void) 328270784Sjulian{ 328370784Sjulian hook_p hook; 328470784Sjulian int i = 1; 3285131008Srwatson mtx_lock(&ng_nodelist_mtx); 328670784Sjulian SLIST_FOREACH(hook, &ng_allhooks, hk_all) { 328770784Sjulian printf("[%d] ", i++); 328870784Sjulian dumphook(hook, NULL, 0); 328970784Sjulian } 3290131008Srwatson mtx_unlock(&ng_nodelist_mtx); 329170784Sjulian} 329270784Sjulian 329370700Sjulianstatic int 329470700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS) 329570700Sjulian{ 329670700Sjulian int error; 329770700Sjulian int val; 329870700Sjulian int i; 329970700Sjulian 330070700Sjulian val = allocated; 330170700Sjulian i = 1; 330270700Sjulian error = sysctl_handle_int(oidp, &val, sizeof(int), req); 330371047Sjulian if (error != 0 || req->newptr == NULL) 330471047Sjulian return (error); 330571047Sjulian if (val == 42) { 330670784Sjulian ng_dumpitems(); 330770784Sjulian ng_dumpnodes(); 330870784Sjulian ng_dumphooks(); 330970700Sjulian } 331071047Sjulian return (0); 331152419Sjulian} 331252419Sjulian 331371047SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW, 331471047Sjulian 0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items"); 331570784Sjulian#endif /* NETGRAPH_DEBUG */ 331670700Sjulian 331770700Sjulian 331870700Sjulian/*********************************************************************** 331970700Sjulian* Worklist routines 332070700Sjulian**********************************************************************/ 332170700Sjulian/* NETISR thread enters here */ 332252419Sjulian/* 332370700Sjulian * Pick a node off the list of nodes with work, 332470700Sjulian * try get an item to process off it. 332570700Sjulian * If there are no more, remove the node from the list. 332652419Sjulian */ 332770700Sjulianstatic void 332870700Sjulianngintr(void) 332952419Sjulian{ 333070700Sjulian item_p item; 333170700Sjulian node_p node = NULL; 333252419Sjulian 333370700Sjulian for (;;) { 3334168049Swkoszek NG_WORKLIST_LOCK(); 333570700Sjulian node = TAILQ_FIRST(&ng_worklist); 333670700Sjulian if (!node) { 3337168049Swkoszek NG_WORKLIST_UNLOCK(); 333870700Sjulian break; 333969922Sjulian } 3340132464Sjulian node->nd_flags &= ~NGF_WORKQ; 334170784Sjulian TAILQ_REMOVE(&ng_worklist, node, nd_work); 3342168049Swkoszek NG_WORKLIST_UNLOCK(); 3343154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist", 3344154275Sglebius __func__, node->nd_ID, node); 334570700Sjulian /* 334670700Sjulian * We have the node. We also take over the reference 334770700Sjulian * that the list had on it. 334870700Sjulian * Now process as much as you can, until it won't 334970700Sjulian * let you have another item off the queue. 335070700Sjulian * All this time, keep the reference 335170700Sjulian * that lets us be sure that the node still exists. 335270700Sjulian * Let the reference go at the last minute. 335371902Sjulian * ng_dequeue will put us back on the worklist 335471902Sjulian * if there is more too do. This may be of use if there 3355152451Sglebius * are Multiple Processors and multiple Net threads in the 335671902Sjulian * future. 335770700Sjulian */ 335870700Sjulian for (;;) { 3359151238Sglebius int rw; 3360151238Sglebius 3361168049Swkoszek NG_QUEUE_LOCK(&node->nd_input_queue); 3362151238Sglebius item = ng_dequeue(&node->nd_input_queue, &rw); 336370700Sjulian if (item == NULL) { 3364168049Swkoszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 336570700Sjulian break; /* go look for another node */ 336670700Sjulian } else { 3367168049Swkoszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 336874078Sjulian NGI_GET_NODE(item, node); /* zaps stored node */ 3369151238Sglebius ng_apply_item(node, item, rw); 337074078Sjulian NG_NODE_UNREF(node); 337170700Sjulian } 337270700Sjulian } 337373238Sjulian NG_NODE_UNREF(node); 337452419Sjulian } 337570700Sjulian} 337669922Sjulian 337770700Sjulianstatic void 337870700Sjulianng_worklist_remove(node_p node) 337970700Sjulian{ 3380154225Sglebius mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED); 3381154225Sglebius 3382168049Swkoszek NG_WORKLIST_LOCK(); 3383132464Sjulian if (node->nd_flags & NGF_WORKQ) { 3384132464Sjulian node->nd_flags &= ~NGF_WORKQ; 338570784Sjulian TAILQ_REMOVE(&ng_worklist, node, nd_work); 3386168049Swkoszek NG_WORKLIST_UNLOCK(); 338770784Sjulian NG_NODE_UNREF(node); 3388154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) removed from worklist", 3389154275Sglebius __func__, node->nd_ID, node); 339072979Sjulian } else { 3391168049Swkoszek NG_WORKLIST_UNLOCK(); 339270700Sjulian } 339370700Sjulian} 339470700Sjulian 339572979Sjulian/* 339672979Sjulian * XXX 339772979Sjulian * It's posible that a debugging NG_NODE_REF may need 339872979Sjulian * to be outside the mutex zone 339972979Sjulian */ 340070700Sjulianstatic void 340170700Sjulianng_setisr(node_p node) 340270700Sjulian{ 3403148236Sglebius 3404148266Sglebius mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED); 3405148236Sglebius 3406132464Sjulian if ((node->nd_flags & NGF_WORKQ) == 0) { 340769922Sjulian /* 340870700Sjulian * If we are not already on the work queue, 340970700Sjulian * then put us on. 341069922Sjulian */ 3411132464Sjulian node->nd_flags |= NGF_WORKQ; 3412168049Swkoszek NG_WORKLIST_LOCK(); 341370784Sjulian TAILQ_INSERT_TAIL(&ng_worklist, node, nd_work); 3414168049Swkoszek NG_WORKLIST_UNLOCK(); 341572979Sjulian NG_NODE_REF(node); /* XXX fafe in mutex? */ 3416154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__, 3417154275Sglebius node->nd_ID, node); 3418154225Sglebius } else 3419154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist", 3420154275Sglebius __func__, node->nd_ID, node); 342170700Sjulian schednetisr(NETISR_NETGRAPH); 342270700Sjulian} 342370700Sjulian 342470700Sjulian 342570700Sjulian/*********************************************************************** 342670700Sjulian* Externally useable functions to set up a queue item ready for sending 342770700Sjulian***********************************************************************/ 342870700Sjulian 342970784Sjulian#ifdef NETGRAPH_DEBUG 343070784Sjulian#define ITEM_DEBUG_CHECKS \ 343170700Sjulian do { \ 343271849Sjulian if (NGI_NODE(item) ) { \ 343370700Sjulian printf("item already has node"); \ 3434131933Smarcel kdb_enter("has node"); \ 343571849Sjulian NGI_CLR_NODE(item); \ 343670700Sjulian } \ 343771849Sjulian if (NGI_HOOK(item) ) { \ 343870700Sjulian printf("item already has hook"); \ 3439131933Smarcel kdb_enter("has hook"); \ 344071849Sjulian NGI_CLR_HOOK(item); \ 344170700Sjulian } \ 344270700Sjulian } while (0) 344370700Sjulian#else 344470784Sjulian#define ITEM_DEBUG_CHECKS 344570700Sjulian#endif 344670700Sjulian 344770700Sjulian/* 3448131374Sjulian * Put mbuf into the item. 344970700Sjulian * Hook and node references will be removed when the item is dequeued. 345070700Sjulian * (or equivalent) 345170700Sjulian * (XXX) Unsafe because no reference held by peer on remote node. 345270700Sjulian * remote node might go away in this timescale. 345370700Sjulian * We know the hooks can't go away because that would require getting 345470700Sjulian * a writer item on both nodes and we must have at least a reader 3455151973Sglebius * here to be able to do this. 345670700Sjulian * Note that the hook loaded is the REMOTE hook. 345770700Sjulian * 345870700Sjulian * This is possibly in the critical path for new data. 345970700Sjulian */ 346070700Sjulianitem_p 3461146281Sglebiusng_package_data(struct mbuf *m, int flags) 346270700Sjulian{ 346370700Sjulian item_p item; 346470700Sjulian 3465146281Sglebius if ((item = ng_getqblk(flags)) == NULL) { 346670700Sjulian NG_FREE_M(m); 346770700Sjulian return (NULL); 346870700Sjulian } 346970784Sjulian ITEM_DEBUG_CHECKS; 3470149505Sglebius item->el_flags = NGQF_DATA | NGQF_READER; 347170700Sjulian item->el_next = NULL; 347270700Sjulian NGI_M(item) = m; 347370700Sjulian return (item); 347470700Sjulian} 347570700Sjulian 347670700Sjulian/* 347770700Sjulian * Allocate a queue item and put items into it.. 347870700Sjulian * Evaluate the address as this will be needed to queue it and 347970700Sjulian * to work out what some of the fields should be. 348070700Sjulian * Hook and node references will be removed when the item is dequeued. 348170700Sjulian * (or equivalent) 348270700Sjulian */ 348370700Sjulianitem_p 3484146281Sglebiusng_package_msg(struct ng_mesg *msg, int flags) 348570700Sjulian{ 348670700Sjulian item_p item; 348770700Sjulian 3488146281Sglebius if ((item = ng_getqblk(flags)) == NULL) { 348971849Sjulian NG_FREE_MSG(msg); 349070700Sjulian return (NULL); 349169922Sjulian } 349270784Sjulian ITEM_DEBUG_CHECKS; 3493149505Sglebius /* Messages items count as writers unless explicitly exempted. */ 3494149505Sglebius if (msg->header.cmd & NGM_READONLY) 3495149505Sglebius item->el_flags = NGQF_MESG | NGQF_READER; 3496149505Sglebius else 3497149505Sglebius item->el_flags = NGQF_MESG | NGQF_WRITER; 349870700Sjulian item->el_next = NULL; 349970700Sjulian /* 350070700Sjulian * Set the current lasthook into the queue item 350170700Sjulian */ 350270700Sjulian NGI_MSG(item) = msg; 3503102244Sarchie NGI_RETADDR(item) = 0; 350470700Sjulian return (item); 350570700Sjulian} 350669922Sjulian 350770700Sjulian 350870700Sjulian 350971849Sjulian#define SET_RETADDR(item, here, retaddr) \ 351071047Sjulian do { /* Data or fn items don't have retaddrs */ \ 351171047Sjulian if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) { \ 351270700Sjulian if (retaddr) { \ 351370700Sjulian NGI_RETADDR(item) = retaddr; \ 351470700Sjulian } else { \ 351570700Sjulian /* \ 351670700Sjulian * The old return address should be ok. \ 351770700Sjulian * If there isn't one, use the address \ 351870700Sjulian * here. \ 351970700Sjulian */ \ 352070700Sjulian if (NGI_RETADDR(item) == 0) { \ 352170700Sjulian NGI_RETADDR(item) \ 352270700Sjulian = ng_node2ID(here); \ 352370700Sjulian } \ 352470700Sjulian } \ 352570700Sjulian } \ 352670700Sjulian } while (0) 352770700Sjulian 352870700Sjulianint 352970700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr) 353070700Sjulian{ 353171849Sjulian hook_p peer; 353271849Sjulian node_p peernode; 353370784Sjulian ITEM_DEBUG_CHECKS; 353470700Sjulian /* 353570700Sjulian * Quick sanity check.. 353670784Sjulian * Since a hook holds a reference on it's node, once we know 353770784Sjulian * that the peer is still connected (even if invalid,) we know 353870784Sjulian * that the peer node is present, though maybe invalid. 353970700Sjulian */ 354070700Sjulian if ((hook == NULL) 354170784Sjulian || NG_HOOK_NOT_VALID(hook) 354270784Sjulian || (NG_HOOK_PEER(hook) == NULL) 354370784Sjulian || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)) 354470784Sjulian || NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) { 354570700Sjulian NG_FREE_ITEM(item); 354671047Sjulian TRAP_ERROR(); 354773083Sjulian return (ENETDOWN); 354852419Sjulian } 354952419Sjulian 355070700Sjulian /* 355170700Sjulian * Transfer our interest to the other (peer) end. 355270700Sjulian */ 355371849Sjulian peer = NG_HOOK_PEER(hook); 355471849Sjulian NG_HOOK_REF(peer); 355571849Sjulian NGI_SET_HOOK(item, peer); 355671849Sjulian peernode = NG_PEER_NODE(hook); 355771849Sjulian NG_NODE_REF(peernode); 355871849Sjulian NGI_SET_NODE(item, peernode); 355979706Sjulian SET_RETADDR(item, here, retaddr); 356070700Sjulian return (0); 356170700Sjulian} 356252419Sjulian 356370700Sjulianint 3564152451Sglebiusng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr) 356570700Sjulian{ 3566152451Sglebius node_p dest = NULL; 356770700Sjulian hook_p hook = NULL; 3568152451Sglebius int error; 356970700Sjulian 357070784Sjulian ITEM_DEBUG_CHECKS; 357170700Sjulian /* 357270700Sjulian * Note that ng_path2noderef increments the reference count 357370700Sjulian * on the node for us if it finds one. So we don't have to. 357470700Sjulian */ 357570700Sjulian error = ng_path2noderef(here, address, &dest, &hook); 357670700Sjulian if (error) { 357770700Sjulian NG_FREE_ITEM(item); 357870784Sjulian return (error); 357952419Sjulian } 358071849Sjulian NGI_SET_NODE(item, dest); 358171849Sjulian if ( hook) { 358270784Sjulian NG_HOOK_REF(hook); /* don't let it go while on the queue */ 358371849Sjulian NGI_SET_HOOK(item, hook); 358471849Sjulian } 358571849Sjulian SET_RETADDR(item, here, retaddr); 358670700Sjulian return (0); 358770700Sjulian} 358852419Sjulian 358970700Sjulianint 359070700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr) 359170700Sjulian{ 359270700Sjulian node_p dest; 359370700Sjulian 359470784Sjulian ITEM_DEBUG_CHECKS; 359570700Sjulian /* 359670700Sjulian * Find the target node. 359770700Sjulian */ 359870700Sjulian dest = ng_ID2noderef(ID); /* GETS REFERENCE! */ 359970700Sjulian if (dest == NULL) { 360070700Sjulian NG_FREE_ITEM(item); 360171047Sjulian TRAP_ERROR(); 360270700Sjulian return(EINVAL); 360370700Sjulian } 360470700Sjulian /* Fill out the contents */ 360571849Sjulian NGI_SET_NODE(item, dest); 360671849Sjulian NGI_CLR_HOOK(item); 360771849Sjulian SET_RETADDR(item, here, retaddr); 360852419Sjulian return (0); 360952419Sjulian} 361052419Sjulian 361152419Sjulian/* 361270700Sjulian * special case to send a message to self (e.g. destroy node) 361370700Sjulian * Possibly indicate an arrival hook too. 361470700Sjulian * Useful for removing that hook :-) 361552419Sjulian */ 361670700Sjulianitem_p 361770700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg) 361852419Sjulian{ 361970700Sjulian item_p item; 362052419Sjulian 362170700Sjulian /* 362270700Sjulian * Find the target node. 362370700Sjulian * If there is a HOOK argument, then use that in preference 362470700Sjulian * to the address. 362570700Sjulian */ 3626146281Sglebius if ((item = ng_getqblk(NG_NOFLAGS)) == NULL) { 362771849Sjulian NG_FREE_MSG(msg); 362870700Sjulian return (NULL); 362952419Sjulian } 363070700Sjulian 363170700Sjulian /* Fill out the contents */ 3632149505Sglebius item->el_flags = NGQF_MESG | NGQF_WRITER; 363370700Sjulian item->el_next = NULL; 363470784Sjulian NG_NODE_REF(here); 363571849Sjulian NGI_SET_NODE(item, here); 363671849Sjulian if (hook) { 363770784Sjulian NG_HOOK_REF(hook); 363871849Sjulian NGI_SET_HOOK(item, hook); 363971849Sjulian } 364070700Sjulian NGI_MSG(item) = msg; 364170700Sjulian NGI_RETADDR(item) = ng_node2ID(here); 364270700Sjulian return (item); 364352419Sjulian} 364452419Sjulian 3645146281Sglebiusint 3646146180Sglebiusng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2, 3647146281Sglebius int flags) 364871047Sjulian{ 364971047Sjulian item_p item; 365071047Sjulian 3651146281Sglebius if ((item = ng_getqblk(flags)) == NULL) { 365271047Sjulian return (ENOMEM); 365371047Sjulian } 365471047Sjulian item->el_flags = NGQF_FN | NGQF_WRITER; 365573238Sjulian NG_NODE_REF(node); /* and one for the item */ 365671849Sjulian NGI_SET_NODE(item, node); 365771849Sjulian if (hook) { 365871047Sjulian NG_HOOK_REF(hook); 365971849Sjulian NGI_SET_HOOK(item, hook); 366071047Sjulian } 366171047Sjulian NGI_FN(item) = fn; 366271047Sjulian NGI_ARG1(item) = arg1; 366371047Sjulian NGI_ARG2(item) = arg2; 3664146281Sglebius return(ng_snd_item(item, flags)); 366571047Sjulian} 366671047Sjulian 3667152451Sglebius/* 366891711Sjulian * Official timeout routines for Netgraph nodes. 366991711Sjulian */ 367091711Sjulianstatic void 3671140852Sglebiusng_callout_trampoline(void *arg) 367291711Sjulian{ 367391711Sjulian item_p item = arg; 367491711Sjulian 367591711Sjulian ng_snd_item(item, 0); 367691711Sjulian} 367791711Sjulian 367891711Sjulian 3679137138Sglebiusint 3680138268Sglebiusng_callout(struct callout *c, node_p node, hook_p hook, int ticks, 368191711Sjulian ng_item_fn *fn, void * arg1, int arg2) 368291711Sjulian{ 3683149881Sglebius item_p item, oitem; 368491711Sjulian 3685146281Sglebius if ((item = ng_getqblk(NG_NOFLAGS)) == NULL) 3686137138Sglebius return (ENOMEM); 3687137138Sglebius 368891711Sjulian item->el_flags = NGQF_FN | NGQF_WRITER; 368991711Sjulian NG_NODE_REF(node); /* and one for the item */ 369091711Sjulian NGI_SET_NODE(item, node); 369191711Sjulian if (hook) { 369291711Sjulian NG_HOOK_REF(hook); 369391711Sjulian NGI_SET_HOOK(item, hook); 369491711Sjulian } 369591711Sjulian NGI_FN(item) = fn; 369691711Sjulian NGI_ARG1(item) = arg1; 369791711Sjulian NGI_ARG2(item) = arg2; 3698149881Sglebius oitem = c->c_arg; 3699149881Sglebius if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 && 3700149881Sglebius oitem != NULL) 3701149881Sglebius NG_FREE_ITEM(oitem); 3702137138Sglebius return (0); 370391711Sjulian} 370491711Sjulian 370591711Sjulian/* A special modified version of untimeout() */ 3706152451Sglebiusint 3707138268Sglebiusng_uncallout(struct callout *c, node_p node) 370891711Sjulian{ 370991711Sjulian item_p item; 3710137138Sglebius int rval; 3711149357Sglebius 3712149357Sglebius KASSERT(c != NULL, ("ng_uncallout: NULL callout")); 3713149357Sglebius KASSERT(node != NULL, ("ng_uncallout: NULL node")); 3714149357Sglebius 3715137230Sglebius rval = callout_stop(c); 3716137138Sglebius item = c->c_arg; 3717137138Sglebius /* Do an extra check */ 3718140852Sglebius if ((rval > 0) && (c->c_func == &ng_callout_trampoline) && 3719137138Sglebius (NGI_NODE(item) == node)) { 372091711Sjulian /* 372191711Sjulian * We successfully removed it from the queue before it ran 3722152451Sglebius * So now we need to unreference everything that was 372391711Sjulian * given extra references. (NG_FREE_ITEM does this). 372491711Sjulian */ 372591711Sjulian NG_FREE_ITEM(item); 372691711Sjulian } 3727149881Sglebius c->c_arg = NULL; 3728137138Sglebius 3729137138Sglebius return (rval); 373091711Sjulian} 373191711Sjulian 373270700Sjulian/* 373370700Sjulian * Set the address, if none given, give the node here. 373470700Sjulian */ 373570700Sjulianvoid 373670700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr) 373770700Sjulian{ 373870700Sjulian if (retaddr) { 373970700Sjulian NGI_RETADDR(item) = retaddr; 374070700Sjulian } else { 374170700Sjulian /* 374270700Sjulian * The old return address should be ok. 374370700Sjulian * If there isn't one, use the address here. 374470700Sjulian */ 374570700Sjulian NGI_RETADDR(item) = ng_node2ID(here); 374670700Sjulian } 374770700Sjulian} 374852419Sjulian 374970700Sjulian#define TESTING 375070700Sjulian#ifdef TESTING 375170700Sjulian/* just test all the macros */ 375270700Sjulianvoid 375370700Sjulianng_macro_test(item_p item); 375470700Sjulianvoid 375570700Sjulianng_macro_test(item_p item) 375670700Sjulian{ 375770700Sjulian node_p node = NULL; 375870700Sjulian hook_p hook = NULL; 375970700Sjulian struct mbuf *m; 376070700Sjulian struct ng_mesg *msg; 376170700Sjulian ng_ID_t retaddr; 376270700Sjulian int error; 376370700Sjulian 376470700Sjulian NGI_GET_M(item, m); 376570700Sjulian NGI_GET_MSG(item, msg); 376670700Sjulian retaddr = NGI_RETADDR(item); 3767131374Sjulian NG_SEND_DATA(error, hook, m, NULL); 376870700Sjulian NG_SEND_DATA_ONLY(error, hook, m); 376970700Sjulian NG_FWD_NEW_DATA(error, item, hook, m); 377070784Sjulian NG_FWD_ITEM_HOOK(error, item, hook); 377170700Sjulian NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr); 377270700Sjulian NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr); 377370700Sjulian NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr); 377470700Sjulian NG_FWD_MSG_HOOK(error, node, item, hook, retaddr); 377570700Sjulian} 377670700Sjulian#endif /* TESTING */ 377770700Sjulian 3778