1139823Simp/*- 252419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc. 352419Sjulian * All rights reserved. 470700Sjulian * 552419Sjulian * Subject to the following obligations and disclaimer of warranty, use and 652419Sjulian * redistribution of this software, in source or object code forms, with or 752419Sjulian * without modifications are expressly permitted by Whistle Communications; 852419Sjulian * provided, however, that: 952419Sjulian * 1. Any and all reproductions of the source or object code must include the 1052419Sjulian * copyright notice above and the following disclaimer of warranties; and 1152419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle 1252419Sjulian * Communications, Inc. trademarks, including the mark "WHISTLE 1352419Sjulian * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1452419Sjulian * such appears in the above copyright notice or in the software. 1570700Sjulian * 1652419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 1752419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 1852419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 1952419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2052419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 2152419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2252419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 2352419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2452419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 2552419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 2652419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 2752419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 2852419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 2952419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3052419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3152419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3252419Sjulian * OF SUCH DAMAGE. 3352419Sjulian * 3467506Sjulian * Authors: Julian Elischer <julian@freebsd.org> 3567506Sjulian * Archie Cobbs <archie@freebsd.org> 3652419Sjulian * 3752419Sjulian * $FreeBSD: stable/11/sys/netgraph/ng_base.c 369960 2021-06-15 16:57:32Z git2svn $ 3852419Sjulian * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $ 3952419Sjulian */ 4052419Sjulian 4152419Sjulian/* 4252419Sjulian * This file implements the base netgraph code. 4352419Sjulian */ 4452419Sjulian 4552419Sjulian#include <sys/param.h> 46231766Sglebius#include <sys/systm.h> 47139235Sglebius#include <sys/ctype.h> 48231831Sglebius#include <sys/hash.h> 49131933Smarcel#include <sys/kdb.h> 5052419Sjulian#include <sys/kernel.h> 51231760Sglebius#include <sys/kthread.h> 52154225Sglebius#include <sys/ktr.h> 53114216Skan#include <sys/limits.h> 54230480Sglebius#include <sys/lock.h> 5552419Sjulian#include <sys/malloc.h> 56139235Sglebius#include <sys/mbuf.h> 57231760Sglebius#include <sys/proc.h> 5852419Sjulian#include <sys/queue.h> 59231760Sglebius#include <sys/refcount.h> 60231760Sglebius#include <sys/rwlock.h> 61231760Sglebius#include <sys/smp.h> 6272946Sjulian#include <sys/sysctl.h> 63139235Sglebius#include <sys/syslog.h> 64186093Smav#include <sys/unistd.h> 65177953Smav#include <machine/cpu.h> 66295126Sglebius#include <vm/uma.h> 6752419Sjulian 6852419Sjulian#include <net/netisr.h> 69195699Srwatson#include <net/vnet.h> 7052419Sjulian 7152419Sjulian#include <netgraph/ng_message.h> 7252419Sjulian#include <netgraph/netgraph.h> 7353913Sarchie#include <netgraph/ng_parse.h> 7452419Sjulian 7572053SjulianMODULE_VERSION(netgraph, NG_ABI_VERSION); 7659756Speter 77151974Sglebius/* Mutex to protect topology events. */ 78256550Smelifarostatic struct rwlock ng_topo_lock; 79256550Smelifaro#define TOPOLOGY_RLOCK() rw_rlock(&ng_topo_lock) 80256550Smelifaro#define TOPOLOGY_RUNLOCK() rw_runlock(&ng_topo_lock) 81256550Smelifaro#define TOPOLOGY_WLOCK() rw_wlock(&ng_topo_lock) 82256550Smelifaro#define TOPOLOGY_WUNLOCK() rw_wunlock(&ng_topo_lock) 83256550Smelifaro#define TOPOLOGY_NOTOWNED() rw_assert(&ng_topo_lock, RA_UNLOCKED) 84151974Sglebius 8570784Sjulian#ifdef NETGRAPH_DEBUG 86176802Smavstatic struct mtx ng_nodelist_mtx; /* protects global node/hook lists */ 87152451Sglebiusstatic struct mtx ngq_mtx; /* protects the queue item list */ 8870784Sjulian 8970784Sjulianstatic SLIST_HEAD(, ng_node) ng_allnodes; 9070784Sjulianstatic LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */ 9170784Sjulianstatic SLIST_HEAD(, ng_hook) ng_allhooks; 9270784Sjulianstatic LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */ 9370784Sjulian 9470784Sjulianstatic void ng_dumpitems(void); 9570784Sjulianstatic void ng_dumpnodes(void); 9670784Sjulianstatic void ng_dumphooks(void); 9770784Sjulian 9870784Sjulian#endif /* NETGRAPH_DEBUG */ 9970935Sjulian/* 100152451Sglebius * DEAD versions of the structures. 101249583Sgabor * In order to avoid races, it is sometimes necessary to point 102152451Sglebius * at SOMETHING even though theoretically, the current entity is 10370935Sjulian * INVALID. Use these to avoid these races. 10470935Sjulian */ 10570935Sjulianstruct ng_type ng_deadtype = { 10670935Sjulian NG_ABI_VERSION, 10770935Sjulian "dead", 10870935Sjulian NULL, /* modevent */ 10970935Sjulian NULL, /* constructor */ 11070935Sjulian NULL, /* rcvmsg */ 11170935Sjulian NULL, /* shutdown */ 11270935Sjulian NULL, /* newhook */ 11370935Sjulian NULL, /* findhook */ 11470935Sjulian NULL, /* connect */ 11570935Sjulian NULL, /* rcvdata */ 11670935Sjulian NULL, /* disconnect */ 11770935Sjulian NULL, /* cmdlist */ 11870935Sjulian}; 11970784Sjulian 12070935Sjulianstruct ng_node ng_deadnode = { 12170935Sjulian "dead", 12270935Sjulian &ng_deadtype, 123132464Sjulian NGF_INVALID, 12470935Sjulian 0, /* numhooks */ 12570935Sjulian NULL, /* private */ 12670935Sjulian 0, /* ID */ 127201145Santoine LIST_HEAD_INITIALIZER(ng_deadnode.nd_hooks), 12870935Sjulian {}, /* all_nodes list entry */ 12970935Sjulian {}, /* id hashtable list entry */ 13070935Sjulian { 0, 131178228Smav 0, 13270935Sjulian {}, /* should never use! (should hang) */ 133178228Smav {}, /* workqueue entry */ 134178228Smav STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue), 13570935Sjulian }, 136178228Smav 1, /* refs */ 137191827Szec NULL, /* vnet */ 13870935Sjulian#ifdef NETGRAPH_DEBUG 13970935Sjulian ND_MAGIC, 14070935Sjulian __FILE__, 14170935Sjulian __LINE__, 14270935Sjulian {NULL} 14370935Sjulian#endif /* NETGRAPH_DEBUG */ 14470935Sjulian}; 14570935Sjulian 14670935Sjulianstruct ng_hook ng_deadhook = { 14770935Sjulian "dead", 14870935Sjulian NULL, /* private */ 14970935Sjulian HK_INVALID | HK_DEAD, 150148261Sglebius 0, /* undefined data link type */ 15170935Sjulian &ng_deadhook, /* Peer is self */ 15270935Sjulian &ng_deadnode, /* attached to deadnode */ 15370935Sjulian {}, /* hooks list */ 15471885Sjulian NULL, /* override rcvmsg() */ 15571885Sjulian NULL, /* override rcvdata() */ 156178228Smav 1, /* refs always >= 1 */ 15770935Sjulian#ifdef NETGRAPH_DEBUG 15870935Sjulian HK_MAGIC, 15970935Sjulian __FILE__, 16070935Sjulian __LINE__, 16170935Sjulian {NULL} 16270935Sjulian#endif /* NETGRAPH_DEBUG */ 16370935Sjulian}; 16470935Sjulian 16570935Sjulian/* 16670935Sjulian * END DEAD STRUCTURES 16770935Sjulian */ 16870700Sjulian/* List nodes with unallocated work */ 169178228Smavstatic STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist); 17071902Sjulianstatic struct mtx ng_worklist_mtx; /* MUST LOCK NODE FIRST */ 17170700Sjulian 17252419Sjulian/* List of installed types */ 17370700Sjulianstatic LIST_HEAD(, ng_type) ng_typelist; 174230480Sglebiusstatic struct rwlock ng_typelist_lock; 175230480Sglebius#define TYPELIST_RLOCK() rw_rlock(&ng_typelist_lock) 176230480Sglebius#define TYPELIST_RUNLOCK() rw_runlock(&ng_typelist_lock) 177230480Sglebius#define TYPELIST_WLOCK() rw_wlock(&ng_typelist_lock) 178230480Sglebius#define TYPELIST_WUNLOCK() rw_wunlock(&ng_typelist_lock) 17952419Sjulian 180231831Sglebius/* Hash related definitions. */ 181231831SglebiusLIST_HEAD(nodehash, ng_node); 182231831Sglebiusstatic VNET_DEFINE(struct nodehash *, ng_ID_hash); 183231831Sglebiusstatic VNET_DEFINE(u_long, ng_ID_hmask); 184231831Sglebiusstatic VNET_DEFINE(u_long, ng_nodes); 185231831Sglebiusstatic VNET_DEFINE(struct nodehash *, ng_name_hash); 186231831Sglebiusstatic VNET_DEFINE(u_long, ng_name_hmask); 187231831Sglebiusstatic VNET_DEFINE(u_long, ng_named_nodes); 188231831Sglebius#define V_ng_ID_hash VNET(ng_ID_hash) 189231831Sglebius#define V_ng_ID_hmask VNET(ng_ID_hmask) 190231831Sglebius#define V_ng_nodes VNET(ng_nodes) 191231831Sglebius#define V_ng_name_hash VNET(ng_name_hash) 192231831Sglebius#define V_ng_name_hmask VNET(ng_name_hmask) 193231831Sglebius#define V_ng_named_nodes VNET(ng_named_nodes) 194195699Srwatson 195230480Sglebiusstatic struct rwlock ng_idhash_lock; 196230480Sglebius#define IDHASH_RLOCK() rw_rlock(&ng_idhash_lock) 197230480Sglebius#define IDHASH_RUNLOCK() rw_runlock(&ng_idhash_lock) 198230480Sglebius#define IDHASH_WLOCK() rw_wlock(&ng_idhash_lock) 199230480Sglebius#define IDHASH_WUNLOCK() rw_wunlock(&ng_idhash_lock) 200230480Sglebius 20171354Sjulian/* Method to find a node.. used twice so do it here */ 202231831Sglebius#define NG_IDHASH_FN(ID) ((ID) % (V_ng_ID_hmask + 1)) 20371354Sjulian#define NG_IDHASH_FIND(ID, node) \ 20471354Sjulian do { \ 205230480Sglebius rw_assert(&ng_idhash_lock, RA_LOCKED); \ 206181803Sbz LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)], \ 20771354Sjulian nd_idnodes) { \ 20871354Sjulian if (NG_NODE_IS_VALID(node) \ 20971354Sjulian && (NG_NODE_ID(node) == ID)) { \ 21071354Sjulian break; \ 21171354Sjulian } \ 21271354Sjulian } \ 21371354Sjulian } while (0) 21452722Sjulian 215230480Sglebiusstatic struct rwlock ng_namehash_lock; 216230480Sglebius#define NAMEHASH_RLOCK() rw_rlock(&ng_namehash_lock) 217230480Sglebius#define NAMEHASH_RUNLOCK() rw_runlock(&ng_namehash_lock) 218230480Sglebius#define NAMEHASH_WLOCK() rw_wlock(&ng_namehash_lock) 219230480Sglebius#define NAMEHASH_WUNLOCK() rw_wunlock(&ng_namehash_lock) 220176802Smav 22152419Sjulian/* Internal functions */ 22252419Sjulianstatic int ng_add_hook(node_p node, const char *name, hook_p * hookp); 22370700Sjulianstatic int ng_generic_msg(node_p here, item_p item, hook_p lasthook); 22452722Sjulianstatic ng_ID_t ng_decodeidname(const char *name); 22552419Sjulianstatic int ngb_mod_event(module_t mod, int event, void *data); 226177953Smavstatic void ng_worklist_add(node_p node); 227186093Smavstatic void ngthread(void *); 228170180Sglebiusstatic int ng_apply_item(node_p node, item_p item, int rw); 229178228Smavstatic void ng_flush_input_queue(node_p node); 23070700Sjulianstatic node_p ng_ID2noderef(ng_ID_t ID); 231172806Smavstatic int ng_con_nodes(item_p item, node_p node, const char *name, 232172806Smav node_p node2, const char *name2); 233172806Smavstatic int ng_con_part2(node_p node, item_p item, hook_p hook); 234172806Smavstatic int ng_con_part3(node_p node, item_p item, hook_p hook); 235231831Sglebiusstatic int ng_mkpeer(node_p node, const char *name, const char *name2, 236231831Sglebius char *type); 237231831Sglebiusstatic void ng_name_rehash(void); 238231831Sglebiusstatic void ng_ID_rehash(void); 23952419Sjulian 240152451Sglebius/* Imported, these used to be externally visible, some may go back. */ 24170700Sjulianvoid ng_destroy_hook(hook_p hook); 24270700Sjulianint ng_path2noderef(node_p here, const char *path, 24370700Sjulian node_p *dest, hook_p *lasthook); 24470700Sjulianint ng_make_node(const char *type, node_p *nodepp); 24570700Sjulianint ng_path_parse(char *addr, char **node, char **path, char **hook); 24671849Sjulianvoid ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3); 24770784Sjulianvoid ng_unname(node_p node); 24870700Sjulian 24952419Sjulian/* Our own netgraph malloc type */ 25052419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); 25170700SjulianMALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage"); 252227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", 253227293Sed "netgraph hook structures"); 254227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", 255227293Sed "netgraph node structures"); 256227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", 257227293Sed "netgraph item structures"); 25852419Sjulian 25970700Sjulian/* Should not be visible outside this file */ 26070784Sjulian 26170784Sjulian#define _NG_ALLOC_HOOK(hook) \ 262184205Sdes hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO) 26370784Sjulian#define _NG_ALLOC_NODE(node) \ 264184205Sdes node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO) 26570784Sjulian 266168049Swkoszek#define NG_QUEUE_LOCK_INIT(n) \ 267168137Swkoszek mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF) 268168049Swkoszek#define NG_QUEUE_LOCK(n) \ 269168137Swkoszek mtx_lock(&(n)->q_mtx) 270168049Swkoszek#define NG_QUEUE_UNLOCK(n) \ 271168137Swkoszek mtx_unlock(&(n)->q_mtx) 272168049Swkoszek#define NG_WORKLIST_LOCK_INIT() \ 273168137Swkoszek mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF) 274168049Swkoszek#define NG_WORKLIST_LOCK() \ 275168137Swkoszek mtx_lock(&ng_worklist_mtx) 276168049Swkoszek#define NG_WORKLIST_UNLOCK() \ 277168137Swkoszek mtx_unlock(&ng_worklist_mtx) 278186093Smav#define NG_WORKLIST_SLEEP() \ 279186093Smav mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0) 280186093Smav#define NG_WORKLIST_WAKEUP() \ 281186093Smav wakeup_one(&ng_worklist) 282168049Swkoszek 28370784Sjulian#ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ 28470784Sjulian/* 28570784Sjulian * In debug mode: 28670784Sjulian * In an attempt to help track reference count screwups 28770784Sjulian * we do not free objects back to the malloc system, but keep them 28870784Sjulian * in a local cache where we can examine them and keep information safely 28970784Sjulian * after they have been freed. 29070784Sjulian * We use this scheme for nodes and hooks, and to some extent for items. 29170784Sjulian */ 29270784Sjulianstatic __inline hook_p 29370784Sjulianng_alloc_hook(void) 29470784Sjulian{ 29570784Sjulian hook_p hook; 29670784Sjulian SLIST_ENTRY(ng_hook) temp; 29772200Sbmilekic mtx_lock(&ng_nodelist_mtx); 29870784Sjulian hook = LIST_FIRST(&ng_freehooks); 29970784Sjulian if (hook) { 30070784Sjulian LIST_REMOVE(hook, hk_hooks); 30170784Sjulian bcopy(&hook->hk_all, &temp, sizeof(temp)); 30270784Sjulian bzero(hook, sizeof(struct ng_hook)); 30370784Sjulian bcopy(&temp, &hook->hk_all, sizeof(temp)); 30472200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 30570784Sjulian hook->hk_magic = HK_MAGIC; 30670784Sjulian } else { 30772200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 30870784Sjulian _NG_ALLOC_HOOK(hook); 30970784Sjulian if (hook) { 31070784Sjulian hook->hk_magic = HK_MAGIC; 31172200Sbmilekic mtx_lock(&ng_nodelist_mtx); 31270784Sjulian SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all); 31372200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 31470784Sjulian } 31570784Sjulian } 31670784Sjulian return (hook); 31770784Sjulian} 31870784Sjulian 31970784Sjulianstatic __inline node_p 32070784Sjulianng_alloc_node(void) 32170784Sjulian{ 32270784Sjulian node_p node; 32370784Sjulian SLIST_ENTRY(ng_node) temp; 32472200Sbmilekic mtx_lock(&ng_nodelist_mtx); 32570784Sjulian node = LIST_FIRST(&ng_freenodes); 32670784Sjulian if (node) { 32770784Sjulian LIST_REMOVE(node, nd_nodes); 32870784Sjulian bcopy(&node->nd_all, &temp, sizeof(temp)); 32970784Sjulian bzero(node, sizeof(struct ng_node)); 33070784Sjulian bcopy(&temp, &node->nd_all, sizeof(temp)); 33172200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 33270784Sjulian node->nd_magic = ND_MAGIC; 33370784Sjulian } else { 33472200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 33570784Sjulian _NG_ALLOC_NODE(node); 33670784Sjulian if (node) { 33770784Sjulian node->nd_magic = ND_MAGIC; 33872200Sbmilekic mtx_lock(&ng_nodelist_mtx); 33970784Sjulian SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all); 34072200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 34170784Sjulian } 34270784Sjulian } 34370784Sjulian return (node); 34470784Sjulian} 34570784Sjulian 34670784Sjulian#define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0) 34770784Sjulian#define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0) 34870784Sjulian 34970784Sjulian#define NG_FREE_HOOK(hook) \ 35070784Sjulian do { \ 351229003Sglebius mtx_lock(&ng_nodelist_mtx); \ 35270784Sjulian LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks); \ 35370784Sjulian hook->hk_magic = 0; \ 354229003Sglebius mtx_unlock(&ng_nodelist_mtx); \ 35570784Sjulian } while (0) 35670784Sjulian 35770784Sjulian#define NG_FREE_NODE(node) \ 35870784Sjulian do { \ 359229003Sglebius mtx_lock(&ng_nodelist_mtx); \ 36070784Sjulian LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes); \ 36170784Sjulian node->nd_magic = 0; \ 362229003Sglebius mtx_unlock(&ng_nodelist_mtx); \ 36370784Sjulian } while (0) 36470784Sjulian 36570784Sjulian#else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ 36670784Sjulian 36770784Sjulian#define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook) 36870784Sjulian#define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node) 36970784Sjulian 370184205Sdes#define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0) 371184205Sdes#define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0) 37270784Sjulian 37370784Sjulian#endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ 37470784Sjulian 375131933Smarcel/* Set this to kdb_enter("X") to catch all errors as they occur */ 37652419Sjulian#ifndef TRAP_ERROR 37771047Sjulian#define TRAP_ERROR() 37852419Sjulian#endif 37952419Sjulian 380215701Sdimstatic VNET_DEFINE(ng_ID_t, nextID) = 1; 381195727Srwatson#define V_nextID VNET(nextID) 38252722Sjulian 38353403Sarchie#ifdef INVARIANTS 38453403Sarchie#define CHECK_DATA_MBUF(m) do { \ 38553403Sarchie struct mbuf *n; \ 38653403Sarchie int total; \ 38753403Sarchie \ 388113255Sdes M_ASSERTPKTHDR(m); \ 389149818Sglebius for (total = 0, n = (m); n != NULL; n = n->m_next) { \ 39053403Sarchie total += n->m_len; \ 391149818Sglebius if (n->m_nextpkt != NULL) \ 392149818Sglebius panic("%s: m_nextpkt", __func__); \ 393149818Sglebius } \ 394149827Sglebius \ 39553403Sarchie if ((m)->m_pkthdr.len != total) { \ 39653403Sarchie panic("%s: %d != %d", \ 39787599Sobrien __func__, (m)->m_pkthdr.len, total); \ 39853403Sarchie } \ 39953403Sarchie } while (0) 40053403Sarchie#else 40153403Sarchie#define CHECK_DATA_MBUF(m) 40253403Sarchie#endif 40352722Sjulian 404172806Smav#define ERROUT(x) do { error = (x); goto done; } while (0) 40553403Sarchie 40652419Sjulian/************************************************************************ 40753913Sarchie Parse type definitions for generic messages 40853913Sarchie************************************************************************/ 40953913Sarchie 41053913Sarchie/* Handy structure parse type defining macro */ 41153913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ 41297685Sarchiestatic const struct ng_parse_struct_field \ 41397685Sarchie ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args; \ 41453913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = { \ 41553913Sarchie &ng_parse_struct_type, \ 41697685Sarchie &ng_ ## lo ## _type_fields \ 41753913Sarchie} 41853913Sarchie 41953913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); 42053913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); 42153913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); 42253913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); 42353913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); 42453913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); 42553913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); 42653913Sarchie 42753913Sarchie/* Get length of an array when the length is stored as a 32 bit 42872645Sasmodai value immediately preceding the array -- as with struct namelist 42953913Sarchie and struct typelist. */ 43053913Sarchiestatic int 43153913Sarchieng_generic_list_getLength(const struct ng_parse_type *type, 43253913Sarchie const u_char *start, const u_char *buf) 43353913Sarchie{ 43453913Sarchie return *((const u_int32_t *)(buf - 4)); 43553913Sarchie} 43653913Sarchie 43753913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */ 43853913Sarchiestatic int 43953913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type, 44053913Sarchie const u_char *start, const u_char *buf) 44153913Sarchie{ 44253913Sarchie const struct hooklist *hl = (const struct hooklist *)start; 44353913Sarchie 44453913Sarchie return hl->nodeinfo.hooks; 44553913Sarchie} 44653913Sarchie 44753913Sarchie/* Array type for a variable length array of struct namelist */ 44853913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = { 44953913Sarchie &ng_generic_nodeinfo_type, 45053913Sarchie &ng_generic_list_getLength 45153913Sarchie}; 45253913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = { 45353913Sarchie &ng_parse_array_type, 45453913Sarchie &ng_nodeinfoarray_type_info 45553913Sarchie}; 45653913Sarchie 45753913Sarchie/* Array type for a variable length array of struct typelist */ 45853913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = { 45953913Sarchie &ng_generic_typeinfo_type, 46053913Sarchie &ng_generic_list_getLength 46153913Sarchie}; 46253913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = { 46353913Sarchie &ng_parse_array_type, 46453913Sarchie &ng_typeinfoarray_type_info 46553913Sarchie}; 46653913Sarchie 46753913Sarchie/* Array type for array of struct linkinfo in struct hooklist */ 46853913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { 46953913Sarchie &ng_generic_linkinfo_type, 47053913Sarchie &ng_generic_linkinfo_getLength 47153913Sarchie}; 47253913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = { 47353913Sarchie &ng_parse_array_type, 47453913Sarchie &ng_generic_linkinfo_array_type_info 47553913Sarchie}; 47653913Sarchie 477260046SglebiusDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_typeinfoarray_type)); 47853913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, 47953913Sarchie (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); 48053913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, 48153913Sarchie (&ng_generic_nodeinfoarray_type)); 48253913Sarchie 48353913Sarchie/* List of commands and how to convert arguments to/from ASCII */ 48453913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = { 48553913Sarchie { 48653913Sarchie NGM_GENERIC_COOKIE, 48753913Sarchie NGM_SHUTDOWN, 48853913Sarchie "shutdown", 48953913Sarchie NULL, 49053913Sarchie NULL 49153913Sarchie }, 49253913Sarchie { 49353913Sarchie NGM_GENERIC_COOKIE, 49453913Sarchie NGM_MKPEER, 49553913Sarchie "mkpeer", 49653913Sarchie &ng_generic_mkpeer_type, 49753913Sarchie NULL 49853913Sarchie }, 49953913Sarchie { 50053913Sarchie NGM_GENERIC_COOKIE, 50153913Sarchie NGM_CONNECT, 50253913Sarchie "connect", 50353913Sarchie &ng_generic_connect_type, 50453913Sarchie NULL 50553913Sarchie }, 50653913Sarchie { 50753913Sarchie NGM_GENERIC_COOKIE, 50853913Sarchie NGM_NAME, 50953913Sarchie "name", 51053913Sarchie &ng_generic_name_type, 51153913Sarchie NULL 51253913Sarchie }, 51353913Sarchie { 51453913Sarchie NGM_GENERIC_COOKIE, 51553913Sarchie NGM_RMHOOK, 51653913Sarchie "rmhook", 51753913Sarchie &ng_generic_rmhook_type, 51853913Sarchie NULL 51953913Sarchie }, 52053913Sarchie { 52153913Sarchie NGM_GENERIC_COOKIE, 52253913Sarchie NGM_NODEINFO, 52353913Sarchie "nodeinfo", 52453913Sarchie NULL, 52553913Sarchie &ng_generic_nodeinfo_type 52653913Sarchie }, 52753913Sarchie { 52853913Sarchie NGM_GENERIC_COOKIE, 52953913Sarchie NGM_LISTHOOKS, 53053913Sarchie "listhooks", 53153913Sarchie NULL, 53253913Sarchie &ng_generic_hooklist_type 53353913Sarchie }, 53453913Sarchie { 53553913Sarchie NGM_GENERIC_COOKIE, 53653913Sarchie NGM_LISTNAMES, 53753913Sarchie "listnames", 53853913Sarchie NULL, 53953913Sarchie &ng_generic_listnodes_type /* same as NGM_LISTNODES */ 54053913Sarchie }, 54153913Sarchie { 54253913Sarchie NGM_GENERIC_COOKIE, 54353913Sarchie NGM_LISTNODES, 54453913Sarchie "listnodes", 54553913Sarchie NULL, 54653913Sarchie &ng_generic_listnodes_type 54753913Sarchie }, 54853913Sarchie { 54953913Sarchie NGM_GENERIC_COOKIE, 55053913Sarchie NGM_LISTTYPES, 55153913Sarchie "listtypes", 55253913Sarchie NULL, 553260046Sglebius &ng_generic_typelist_type 55453913Sarchie }, 55553913Sarchie { 55653913Sarchie NGM_GENERIC_COOKIE, 55762471Sphk NGM_TEXT_CONFIG, 55862471Sphk "textconfig", 55962471Sphk NULL, 56062471Sphk &ng_parse_string_type 56162471Sphk }, 56262471Sphk { 56362471Sphk NGM_GENERIC_COOKIE, 56453913Sarchie NGM_TEXT_STATUS, 56553913Sarchie "textstatus", 56653913Sarchie NULL, 56753913Sarchie &ng_parse_string_type 56853913Sarchie }, 56953913Sarchie { 57053913Sarchie NGM_GENERIC_COOKIE, 57153913Sarchie NGM_ASCII2BINARY, 57253913Sarchie "ascii2binary", 57353913Sarchie &ng_parse_ng_mesg_type, 57453913Sarchie &ng_parse_ng_mesg_type 57553913Sarchie }, 57653913Sarchie { 57753913Sarchie NGM_GENERIC_COOKIE, 57853913Sarchie NGM_BINARY2ASCII, 57953913Sarchie "binary2ascii", 58053913Sarchie &ng_parse_ng_mesg_type, 58153913Sarchie &ng_parse_ng_mesg_type 58253913Sarchie }, 58353913Sarchie { 0 } 58453913Sarchie}; 58553913Sarchie 58653913Sarchie/************************************************************************ 58752419Sjulian Node routines 58852419Sjulian************************************************************************/ 58952419Sjulian 59052419Sjulian/* 59152419Sjulian * Instantiate a node of the requested type 59252419Sjulian */ 59352419Sjulianint 59452419Sjulianng_make_node(const char *typename, node_p *nodepp) 59552419Sjulian{ 59652419Sjulian struct ng_type *type; 59770700Sjulian int error; 59852419Sjulian 59952419Sjulian /* Check that the type makes sense */ 60052419Sjulian if (typename == NULL) { 60171047Sjulian TRAP_ERROR(); 60252419Sjulian return (EINVAL); 60352419Sjulian } 60452419Sjulian 605132705Sglebius /* Locate the node type. If we fail we return. Do not try to load 606132705Sglebius * module. 607132705Sglebius */ 608132705Sglebius if ((type = ng_findtype(typename)) == NULL) 609132705Sglebius return (ENXIO); 61052419Sjulian 61170700Sjulian /* 61270700Sjulian * If we have a constructor, then make the node and 61370700Sjulian * call the constructor to do type specific initialisation. 61470700Sjulian */ 61570700Sjulian if (type->constructor != NULL) { 61670700Sjulian if ((error = ng_make_node_common(type, nodepp)) == 0) { 617220745Sglebius if ((error = ((*type->constructor)(*nodepp))) != 0) { 61870784Sjulian NG_NODE_UNREF(*nodepp); 61970700Sjulian } 62070700Sjulian } 62170700Sjulian } else { 62270700Sjulian /* 62370700Sjulian * Node has no constructor. We cannot ask for one 624167677Srwatson * to be made. It must be brought into existence by 62570935Sjulian * some external agency. The external agency should 62670700Sjulian * call ng_make_node_common() directly to get the 62770700Sjulian * netgraph part initialised. 62870700Sjulian */ 62971047Sjulian TRAP_ERROR(); 63070700Sjulian error = EINVAL; 63170700Sjulian } 63270700Sjulian return (error); 63352419Sjulian} 63452419Sjulian 63552419Sjulian/* 63670700Sjulian * Generic node creation. Called by node initialisation for externally 63770700Sjulian * instantiated nodes (e.g. hardware, sockets, etc ). 63852419Sjulian * The returned node has a reference count of 1. 63952419Sjulian */ 64052419Sjulianint 64152419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp) 64252419Sjulian{ 64352419Sjulian node_p node; 64452419Sjulian 64552419Sjulian /* Require the node type to have been already installed */ 64652419Sjulian if (ng_findtype(type->name) == NULL) { 64771047Sjulian TRAP_ERROR(); 64852419Sjulian return (EINVAL); 64952419Sjulian } 65052419Sjulian 65152419Sjulian /* Make a node and try attach it to the type */ 65270784Sjulian NG_ALLOC_NODE(node); 65352419Sjulian if (node == NULL) { 65471047Sjulian TRAP_ERROR(); 65552419Sjulian return (ENOMEM); 65652419Sjulian } 65770784Sjulian node->nd_type = type; 658193731Szec#ifdef VIMAGE 659193731Szec node->nd_vnet = curvnet; 660193731Szec#endif 66170784Sjulian NG_NODE_REF(node); /* note reference */ 66252419Sjulian type->refs++; 66352419Sjulian 664168049Swkoszek NG_QUEUE_LOCK_INIT(&node->nd_input_queue); 665178228Smav STAILQ_INIT(&node->nd_input_queue.queue); 66670784Sjulian node->nd_input_queue.q_flags = 0; 66752419Sjulian 66852419Sjulian /* Initialize hook list for new node */ 66970784Sjulian LIST_INIT(&node->nd_hooks); 67052419Sjulian 671231831Sglebius /* Get an ID and put us in the hash chain. */ 672230480Sglebius IDHASH_WLOCK(); 67370784Sjulian for (;;) { /* wrap protection, even if silly */ 67470700Sjulian node_p node2 = NULL; 675181887Sjulian node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */ 67671354Sjulian 67770784Sjulian /* Is there a problem with the new number? */ 67871354Sjulian NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */ 67971354Sjulian if ((node->nd_ID != 0) && (node2 == NULL)) { 68070784Sjulian break; 68170700Sjulian } 68270784Sjulian } 683231831Sglebius V_ng_nodes++; 684231831Sglebius if (V_ng_nodes * 2 > V_ng_ID_hmask) 685231831Sglebius ng_ID_rehash(); 686229003Sglebius LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], node, 687229003Sglebius nd_idnodes); 688230480Sglebius IDHASH_WUNLOCK(); 68952722Sjulian 69052419Sjulian /* Done */ 69152419Sjulian *nodepp = node; 69252419Sjulian return (0); 69352419Sjulian} 69452419Sjulian 69552419Sjulian/* 69652419Sjulian * Forceably start the shutdown process on a node. Either call 697167677Srwatson * its shutdown method, or do the default shutdown if there is 69852419Sjulian * no type-specific method. 69952419Sjulian * 700167677Srwatson * We can only be called from a shutdown message, so we know we have 70170939Sjulian * a writer lock, and therefore exclusive access. It also means 70270939Sjulian * that we should not be on the work queue, but we check anyhow. 70370700Sjulian * 70470700Sjulian * Persistent node types must have a type-specific method which 705167677Srwatson * allocates a new node in which case, this one is irretrievably going away, 70670939Sjulian * or cleans up anything it needs, and just makes the node valid again, 707152451Sglebius * in which case we allow the node to survive. 70870939Sjulian * 709167677Srwatson * XXX We need to think of how to tell a persistent node that we 71070939Sjulian * REALLY need to go away because the hardware has gone or we 71170939Sjulian * are rebooting.... etc. 71252419Sjulian */ 71352419Sjulianvoid 71471849Sjulianng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3) 71552419Sjulian{ 71670939Sjulian hook_p hook; 71770939Sjulian 71852419Sjulian /* Check if it's already shutting down */ 719132464Sjulian if ((node->nd_flags & NGF_CLOSING) != 0) 72052419Sjulian return; 72152419Sjulian 72271849Sjulian if (node == &ng_deadnode) { 72371849Sjulian printf ("shutdown called on deadnode\n"); 72471849Sjulian return; 72571849Sjulian } 72671849Sjulian 72752419Sjulian /* Add an extra reference so it doesn't go away during this */ 72870784Sjulian NG_NODE_REF(node); 72952419Sjulian 73070784Sjulian /* 73170784Sjulian * Mark it invalid so any newcomers know not to try use it 73270784Sjulian * Also add our own mark so we can't recurse 733132464Sjulian * note that NGF_INVALID does not do this as it's also set during 73470784Sjulian * creation 73570784Sjulian */ 736132464Sjulian node->nd_flags |= NGF_INVALID|NGF_CLOSING; 73752419Sjulian 738129836Sjulian /* If node has its pre-shutdown method, then call it first*/ 739129836Sjulian if (node->nd_type && node->nd_type->close) 740129836Sjulian (*node->nd_type->close)(node); 741129836Sjulian 74270939Sjulian /* Notify all remaining connected nodes to disconnect */ 74370939Sjulian while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL) 74470939Sjulian ng_destroy_hook(hook); 74570784Sjulian 74670700Sjulian /* 74770700Sjulian * Drain the input queue forceably. 74870784Sjulian * it has no hooks so what's it going to do, bleed on someone? 74970784Sjulian * Theoretically we came here from a queue entry that was added 75070784Sjulian * Just before the queue was closed, so it should be empty anyway. 75171902Sjulian * Also removes us from worklist if needed. 75270700Sjulian */ 753178228Smav ng_flush_input_queue(node); 75470700Sjulian 75552419Sjulian /* Ask the type if it has anything to do in this case */ 75670784Sjulian if (node->nd_type && node->nd_type->shutdown) { 75770784Sjulian (*node->nd_type->shutdown)(node); 75871849Sjulian if (NG_NODE_IS_VALID(node)) { 75971849Sjulian /* 76071849Sjulian * Well, blow me down if the node code hasn't declared 76171849Sjulian * that it doesn't want to die. 762298813Spfg * Presumably it is a persistent node. 76371849Sjulian * If we REALLY want it to go away, 76471849Sjulian * e.g. hardware going away, 765132464Sjulian * Our caller should set NGF_REALLY_DIE in nd_flags. 766152451Sglebius */ 767132464Sjulian node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING); 76871849Sjulian NG_NODE_UNREF(node); /* Assume they still have theirs */ 76971849Sjulian return; 77071849Sjulian } 77170700Sjulian } else { /* do the default thing */ 77270784Sjulian NG_NODE_UNREF(node); 77352419Sjulian } 77452419Sjulian 77570784Sjulian ng_unname(node); /* basically a NOP these days */ 77670784Sjulian 77770784Sjulian /* 77870784Sjulian * Remove extra reference, possibly the last 77970784Sjulian * Possible other holders of references may include 78070784Sjulian * timeout callouts, but theoretically the node's supposed to 78170784Sjulian * have cancelled them. Possibly hardware dependencies may 78270784Sjulian * force a driver to 'linger' with a reference. 78370784Sjulian */ 78470784Sjulian NG_NODE_UNREF(node); 78552419Sjulian} 78652419Sjulian 78752419Sjulian/* 78874078Sjulian * Remove a reference to the node, possibly the last. 78974078Sjulian * deadnode always acts as it it were the last. 79052419Sjulian */ 791223754Sglebiusvoid 79270784Sjulianng_unref_node(node_p node) 79352419Sjulian{ 79471047Sjulian 795223754Sglebius if (node == &ng_deadnode) 796223754Sglebius return; 79771047Sjulian 798253346Srodrigc CURVNET_SET(node->nd_vnet); 799253346Srodrigc 800223754Sglebius if (refcount_release(&node->nd_refs)) { /* we were the last */ 80169519Sjulian 80270784Sjulian node->nd_type->refs--; /* XXX maybe should get types lock? */ 803230480Sglebius NAMEHASH_WLOCK(); 804231831Sglebius if (NG_NODE_HAS_NAME(node)) { 805231831Sglebius V_ng_named_nodes--; 806231831Sglebius LIST_REMOVE(node, nd_nodes); 807231831Sglebius } 808230480Sglebius NAMEHASH_WUNLOCK(); 80970700Sjulian 810230480Sglebius IDHASH_WLOCK(); 811231831Sglebius V_ng_nodes--; 81270784Sjulian LIST_REMOVE(node, nd_idnodes); 813230480Sglebius IDHASH_WUNLOCK(); 81452419Sjulian 81570791Sjulian mtx_destroy(&node->nd_input_queue.q_mtx); 81670700Sjulian NG_FREE_NODE(node); 81752419Sjulian } 818253346Srodrigc CURVNET_RESTORE(); 81952419Sjulian} 82052419Sjulian 82152419Sjulian/************************************************************************ 82252722Sjulian Node ID handling 82352722Sjulian************************************************************************/ 82452722Sjulianstatic node_p 82570700Sjulianng_ID2noderef(ng_ID_t ID) 82652722Sjulian{ 82770784Sjulian node_p node; 828231831Sglebius 829230480Sglebius IDHASH_RLOCK(); 83071354Sjulian NG_IDHASH_FIND(ID, node); 831231831Sglebius if (node) 83270784Sjulian NG_NODE_REF(node); 833230480Sglebius IDHASH_RUNLOCK(); 83470784Sjulian return(node); 83552722Sjulian} 83652722Sjulian 83752722Sjulianng_ID_t 83852722Sjulianng_node2ID(node_p node) 83952722Sjulian{ 84070912Sjulian return (node ? NG_NODE_ID(node) : 0); 84152722Sjulian} 84252722Sjulian 84352722Sjulian/************************************************************************ 84452419Sjulian Node name handling 84552419Sjulian************************************************************************/ 84652419Sjulian 84752419Sjulian/* 848229003Sglebius * Assign a node a name. 84952419Sjulian */ 85052419Sjulianint 85152419Sjulianng_name_node(node_p node, const char *name) 85252419Sjulian{ 853231831Sglebius uint32_t hash; 85470700Sjulian node_p node2; 855231831Sglebius int i; 85652419Sjulian 857369960Sgit2svn /* Rename without change is a noop */ 858369960Sgit2svn if (strcmp(NG_NODE_NAME(node), name) == 0) 859369960Sgit2svn return (0); 860369960Sgit2svn 86152419Sjulian /* Check the name is valid */ 862125028Sharti for (i = 0; i < NG_NODESIZ; i++) { 86352419Sjulian if (name[i] == '\0' || name[i] == '.' || name[i] == ':') 86452419Sjulian break; 86552419Sjulian } 86652419Sjulian if (i == 0 || name[i] != '\0') { 86771047Sjulian TRAP_ERROR(); 86852419Sjulian return (EINVAL); 86952419Sjulian } 87052722Sjulian if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ 87171047Sjulian TRAP_ERROR(); 87252419Sjulian return (EINVAL); 87352419Sjulian } 87452419Sjulian 875231831Sglebius NAMEHASH_WLOCK(); 876231831Sglebius if (V_ng_named_nodes * 2 > V_ng_name_hmask) 877231831Sglebius ng_name_rehash(); 87852419Sjulian 879231831Sglebius hash = hash32_str(name, HASHINIT) & V_ng_name_hmask; 880231831Sglebius /* Check the name isn't already being used. */ 881231831Sglebius LIST_FOREACH(node2, &V_ng_name_hash[hash], nd_nodes) 882231831Sglebius if (NG_NODE_IS_VALID(node2) && 883231831Sglebius (strcmp(NG_NODE_NAME(node2), name) == 0)) { 884231831Sglebius NAMEHASH_WUNLOCK(); 885231831Sglebius return (EADDRINUSE); 886231831Sglebius } 887231831Sglebius 888231831Sglebius if (NG_NODE_HAS_NAME(node)) 889231831Sglebius LIST_REMOVE(node, nd_nodes); 890231831Sglebius else 891231831Sglebius V_ng_named_nodes++; 892231831Sglebius /* Copy it. */ 893125028Sharti strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ); 894176802Smav /* Update name hash. */ 895181803Sbz LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes); 896230480Sglebius NAMEHASH_WUNLOCK(); 897176802Smav 89852419Sjulian return (0); 89952419Sjulian} 90052419Sjulian 90152419Sjulian/* 90252419Sjulian * Find a node by absolute name. The name should NOT end with ':' 90352419Sjulian * The name "." means "this node" and "[xxx]" means "the node 90452419Sjulian * with ID (ie, at address) xxx". 90552419Sjulian * 90652419Sjulian * Returns the node if found, else NULL. 90770700Sjulian * Eventually should add something faster than a sequential search. 908170035Srwatson * Note it acquires a reference on the node so you can be sure it's still 909170035Srwatson * there. 91052419Sjulian */ 91152419Sjuliannode_p 91270700Sjulianng_name2noderef(node_p here, const char *name) 91352419Sjulian{ 91452722Sjulian node_p node; 91552722Sjulian ng_ID_t temp; 916176802Smav int hash; 91752419Sjulian 91852419Sjulian /* "." means "this node" */ 91970700Sjulian if (strcmp(name, ".") == 0) { 92070784Sjulian NG_NODE_REF(here); 92170700Sjulian return(here); 92270700Sjulian } 92352419Sjulian 92452419Sjulian /* Check for name-by-ID */ 92552722Sjulian if ((temp = ng_decodeidname(name)) != 0) { 92670700Sjulian return (ng_ID2noderef(temp)); 92752419Sjulian } 92852419Sjulian 929231831Sglebius /* Find node by name. */ 930231831Sglebius hash = hash32_str(name, HASHINIT) & V_ng_name_hmask; 931230480Sglebius NAMEHASH_RLOCK(); 932230480Sglebius LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes) 933176802Smav if (NG_NODE_IS_VALID(node) && 934176802Smav (strcmp(NG_NODE_NAME(node), name) == 0)) { 935230480Sglebius NG_NODE_REF(node); 93652419Sjulian break; 93770912Sjulian } 938230480Sglebius NAMEHASH_RUNLOCK(); 939230480Sglebius 94052419Sjulian return (node); 94152419Sjulian} 94252419Sjulian 94352419Sjulian/* 944108533Sschweikh * Decode an ID name, eg. "[f03034de]". Returns 0 if the 94552722Sjulian * string is not valid, otherwise returns the value. 94652419Sjulian */ 94752722Sjulianstatic ng_ID_t 94852419Sjulianng_decodeidname(const char *name) 94952419Sjulian{ 95052816Sarchie const int len = strlen(name); 95153648Sarchie char *eptr; 95252816Sarchie u_long val; 95352419Sjulian 95452816Sarchie /* Check for proper length, brackets, no leading junk */ 955229003Sglebius if ((len < 3) || (name[0] != '[') || (name[len - 1] != ']') || 956229003Sglebius (!isxdigit(name[1]))) 95770912Sjulian return ((ng_ID_t)0); 95852419Sjulian 95952816Sarchie /* Decode number */ 96052816Sarchie val = strtoul(name + 1, &eptr, 16); 961229003Sglebius if ((eptr - name != len - 1) || (val == ULONG_MAX) || (val == 0)) 96253042Sjulian return ((ng_ID_t)0); 963229003Sglebius 964229003Sglebius return ((ng_ID_t)val); 96552419Sjulian} 96652419Sjulian 96752419Sjulian/* 96852419Sjulian * Remove a name from a node. This should only be called 96952419Sjulian * when shutting down and removing the node. 97052419Sjulian */ 97152419Sjulianvoid 97252419Sjulianng_unname(node_p node) 97352419Sjulian{ 97452419Sjulian} 97552419Sjulian 976231831Sglebius/* 977231831Sglebius * Allocate a bigger name hash. 978231831Sglebius */ 979231831Sglebiusstatic void 980231831Sglebiusng_name_rehash() 981231831Sglebius{ 982231831Sglebius struct nodehash *new; 983231831Sglebius uint32_t hash; 984231831Sglebius u_long hmask; 985231831Sglebius node_p node, node2; 986231831Sglebius int i; 987231831Sglebius 988231831Sglebius new = hashinit_flags((V_ng_name_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask, 989231831Sglebius HASH_NOWAIT); 990231831Sglebius if (new == NULL) 991231831Sglebius return; 992231831Sglebius 993231831Sglebius for (i = 0; i <= V_ng_name_hmask; i++) 994231831Sglebius LIST_FOREACH_SAFE(node, &V_ng_name_hash[i], nd_nodes, node2) { 995231831Sglebius#ifdef INVARIANTS 996231831Sglebius LIST_REMOVE(node, nd_nodes); 997231831Sglebius#endif 998231831Sglebius hash = hash32_str(NG_NODE_NAME(node), HASHINIT) & hmask; 999231831Sglebius LIST_INSERT_HEAD(&new[hash], node, nd_nodes); 1000231831Sglebius } 1001231831Sglebius 1002231831Sglebius hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask); 1003231831Sglebius V_ng_name_hash = new; 1004231831Sglebius V_ng_name_hmask = hmask; 1005231831Sglebius} 1006231831Sglebius 1007231831Sglebius/* 1008231831Sglebius * Allocate a bigger ID hash. 1009231831Sglebius */ 1010231831Sglebiusstatic void 1011231831Sglebiusng_ID_rehash() 1012231831Sglebius{ 1013231831Sglebius struct nodehash *new; 1014231831Sglebius uint32_t hash; 1015231831Sglebius u_long hmask; 1016231831Sglebius node_p node, node2; 1017231831Sglebius int i; 1018231831Sglebius 1019231831Sglebius new = hashinit_flags((V_ng_ID_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask, 1020231831Sglebius HASH_NOWAIT); 1021231831Sglebius if (new == NULL) 1022231831Sglebius return; 1023231831Sglebius 1024231831Sglebius for (i = 0; i <= V_ng_ID_hmask; i++) 1025231831Sglebius LIST_FOREACH_SAFE(node, &V_ng_ID_hash[i], nd_idnodes, node2) { 1026231831Sglebius#ifdef INVARIANTS 1027231831Sglebius LIST_REMOVE(node, nd_idnodes); 1028231831Sglebius#endif 1029231831Sglebius hash = (node->nd_ID % (hmask + 1)); 1030231831Sglebius LIST_INSERT_HEAD(&new[hash], node, nd_idnodes); 1031231831Sglebius } 1032231831Sglebius 1033231831Sglebius hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_name_hmask); 1034231831Sglebius V_ng_ID_hash = new; 1035231831Sglebius V_ng_ID_hmask = hmask; 1036231831Sglebius} 1037231831Sglebius 103852419Sjulian/************************************************************************ 103952419Sjulian Hook routines 104052419Sjulian Names are not optional. Hooks are always connected, except for a 104170939Sjulian brief moment within these routines. On invalidation or during creation 104270939Sjulian they are connected to the 'dead' hook. 104352419Sjulian************************************************************************/ 104452419Sjulian 104552419Sjulian/* 104652419Sjulian * Remove a hook reference 104752419Sjulian */ 104870784Sjulianvoid 104952419Sjulianng_unref_hook(hook_p hook) 105052419Sjulian{ 105171047Sjulian 1052223754Sglebius if (hook == &ng_deadhook) 105371047Sjulian return; 105469519Sjulian 1055223754Sglebius if (refcount_release(&hook->hk_refs)) { /* we were the last */ 1056177722Smav if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */ 105771047Sjulian _NG_NODE_UNREF((_NG_HOOK_NODE(hook))); 105870700Sjulian NG_FREE_HOOK(hook); 105970700Sjulian } 106052419Sjulian} 106152419Sjulian 106252419Sjulian/* 106352419Sjulian * Add an unconnected hook to a node. Only used internally. 106470939Sjulian * Assumes node is locked. (XXX not yet true ) 106552419Sjulian */ 106652419Sjulianstatic int 106752419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp) 106852419Sjulian{ 106952419Sjulian hook_p hook; 107052419Sjulian int error = 0; 107152419Sjulian 107252419Sjulian /* Check that the given name is good */ 107352419Sjulian if (name == NULL) { 107471047Sjulian TRAP_ERROR(); 107552419Sjulian return (EINVAL); 107652419Sjulian } 107754096Sarchie if (ng_findhook(node, name) != NULL) { 107871047Sjulian TRAP_ERROR(); 107954096Sarchie return (EEXIST); 108052419Sjulian } 108152419Sjulian 108252419Sjulian /* Allocate the hook and link it up */ 108370784Sjulian NG_ALLOC_HOOK(hook); 108452419Sjulian if (hook == NULL) { 108571047Sjulian TRAP_ERROR(); 108652419Sjulian return (ENOMEM); 108752419Sjulian } 108870939Sjulian hook->hk_refs = 1; /* add a reference for us to return */ 108970784Sjulian hook->hk_flags = HK_INVALID; 109070939Sjulian hook->hk_peer = &ng_deadhook; /* start off this way */ 109170784Sjulian hook->hk_node = node; 109270784Sjulian NG_NODE_REF(node); /* each hook counts as a reference */ 109352419Sjulian 109470939Sjulian /* Set hook name */ 1095125028Sharti strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ); 109670939Sjulian 109770939Sjulian /* 109870939Sjulian * Check if the node type code has something to say about it 109970939Sjulian * If it fails, the unref of the hook will also unref the node. 110070939Sjulian */ 110170935Sjulian if (node->nd_type->newhook != NULL) { 110270935Sjulian if ((error = (*node->nd_type->newhook)(node, hook, name))) { 110370784Sjulian NG_HOOK_UNREF(hook); /* this frees the hook */ 110470700Sjulian return (error); 110570700Sjulian } 110670935Sjulian } 110752419Sjulian /* 110852419Sjulian * The 'type' agrees so far, so go ahead and link it in. 110952419Sjulian * We'll ask again later when we actually connect the hooks. 111052419Sjulian */ 111170784Sjulian LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 111270784Sjulian node->nd_numhooks++; 111370939Sjulian NG_HOOK_REF(hook); /* one for the node */ 111452419Sjulian 111552419Sjulian if (hookp) 111652419Sjulian *hookp = hook; 111770939Sjulian return (0); 111852419Sjulian} 111952419Sjulian 112052419Sjulian/* 112154096Sarchie * Find a hook 112254096Sarchie * 112354096Sarchie * Node types may supply their own optimized routines for finding 112454096Sarchie * hooks. If none is supplied, we just do a linear search. 112570939Sjulian * XXX Possibly we should add a reference to the hook? 112654096Sarchie */ 112754096Sarchiehook_p 112854096Sarchieng_findhook(node_p node, const char *name) 112954096Sarchie{ 113054096Sarchie hook_p hook; 113154096Sarchie 113270784Sjulian if (node->nd_type->findhook != NULL) 113370784Sjulian return (*node->nd_type->findhook)(node, name); 113470784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 1135229003Sglebius if (NG_HOOK_IS_VALID(hook) && 1136229003Sglebius (strcmp(NG_HOOK_NAME(hook), name) == 0)) 113754096Sarchie return (hook); 113854096Sarchie } 113954096Sarchie return (NULL); 114054096Sarchie} 114154096Sarchie 114254096Sarchie/* 114352419Sjulian * Destroy a hook 114452419Sjulian * 114552419Sjulian * As hooks are always attached, this really destroys two hooks. 114652419Sjulian * The one given, and the one attached to it. Disconnect the hooks 114770939Sjulian * from each other first. We reconnect the peer hook to the 'dead' 114870939Sjulian * hook so that it can still exist after we depart. We then 114970939Sjulian * send the peer its own destroy message. This ensures that we only 1150152451Sglebius * interact with the peer's structures when it is locked processing that 115170939Sjulian * message. We hold a reference to the peer hook so we are guaranteed that 115270939Sjulian * the peer hook and node are still going to exist until 115370939Sjulian * we are finished there as the hook holds a ref on the node. 1154152451Sglebius * We run this same code again on the peer hook, but that time it is already 1155152451Sglebius * attached to the 'dead' hook. 115671047Sjulian * 1157152451Sglebius * This routine is called at all stages of hook creation 115871047Sjulian * on error detection and must be able to handle any such stage. 115952419Sjulian */ 116052419Sjulianvoid 116152419Sjulianng_destroy_hook(hook_p hook) 116252419Sjulian{ 1163151974Sglebius hook_p peer; 1164151974Sglebius node_p node; 116552419Sjulian 116671047Sjulian if (hook == &ng_deadhook) { /* better safe than sorry */ 116771047Sjulian printf("ng_destroy_hook called on deadhook\n"); 116871047Sjulian return; 116971047Sjulian } 1170151974Sglebius 1171151974Sglebius /* 1172151974Sglebius * Protect divorce process with mutex, to avoid races on 1173151974Sglebius * simultaneous disconnect. 1174151974Sglebius */ 1175256550Smelifaro TOPOLOGY_WLOCK(); 1176151974Sglebius 1177151974Sglebius hook->hk_flags |= HK_INVALID; 1178151974Sglebius 1179151974Sglebius peer = NG_HOOK_PEER(hook); 1180151974Sglebius node = NG_HOOK_NODE(hook); 1181151974Sglebius 118270939Sjulian if (peer && (peer != &ng_deadhook)) { 118370939Sjulian /* 118470939Sjulian * Set the peer to point to ng_deadhook 118570939Sjulian * from this moment on we are effectively independent it. 118670939Sjulian * send it an rmhook message of it's own. 118770939Sjulian */ 118870939Sjulian peer->hk_peer = &ng_deadhook; /* They no longer know us */ 118970939Sjulian hook->hk_peer = &ng_deadhook; /* Nor us, them */ 119071047Sjulian if (NG_HOOK_NODE(peer) == &ng_deadnode) { 1191152451Sglebius /* 119271047Sjulian * If it's already divorced from a node, 119371047Sjulian * just free it. 119471047Sjulian */ 1195256550Smelifaro TOPOLOGY_WUNLOCK(); 119671047Sjulian } else { 1197256550Smelifaro TOPOLOGY_WUNLOCK(); 119871047Sjulian ng_rmhook_self(peer); /* Send it a surprise */ 119971047Sjulian } 120070942Sjulian NG_HOOK_UNREF(peer); /* account for peer link */ 120170942Sjulian NG_HOOK_UNREF(hook); /* account for peer link */ 1202151974Sglebius } else 1203256550Smelifaro TOPOLOGY_WUNLOCK(); 120452419Sjulian 1205256550Smelifaro TOPOLOGY_NOTOWNED(); 1206151974Sglebius 120752419Sjulian /* 120852419Sjulian * Remove the hook from the node's list to avoid possible recursion 120952419Sjulian * in case the disconnection results in node shutdown. 121052419Sjulian */ 121171047Sjulian if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */ 121271047Sjulian return; 121371047Sjulian } 121470784Sjulian LIST_REMOVE(hook, hk_hooks); 121570784Sjulian node->nd_numhooks--; 121670784Sjulian if (node->nd_type->disconnect) { 121752419Sjulian /* 121871047Sjulian * The type handler may elect to destroy the node so don't 1219167677Srwatson * trust its existence after this point. (except 122071047Sjulian * that we still hold a reference on it. (which we 122171047Sjulian * inherrited from the hook we are destroying) 122252419Sjulian */ 122370784Sjulian (*node->nd_type->disconnect) (hook); 122452419Sjulian } 122571047Sjulian 122671047Sjulian /* 122771047Sjulian * Note that because we will point to ng_deadnode, the original node 122871047Sjulian * is not decremented automatically so we do that manually. 122971047Sjulian */ 123071047Sjulian _NG_HOOK_NODE(hook) = &ng_deadnode; 123171047Sjulian NG_NODE_UNREF(node); /* We no longer point to it so adjust count */ 123271047Sjulian NG_HOOK_UNREF(hook); /* Account for linkage (in list) to node */ 123352419Sjulian} 123452419Sjulian 123552419Sjulian/* 123652419Sjulian * Take two hooks on a node and merge the connection so that the given node 123752419Sjulian * is effectively bypassed. 123852419Sjulian */ 123952419Sjulianint 124052419Sjulianng_bypass(hook_p hook1, hook_p hook2) 124152419Sjulian{ 124270784Sjulian if (hook1->hk_node != hook2->hk_node) { 124371047Sjulian TRAP_ERROR(); 124452419Sjulian return (EINVAL); 124570784Sjulian } 1246256550Smelifaro TOPOLOGY_WLOCK(); 1247231761Sglebius if (NG_HOOK_NOT_VALID(hook1) || NG_HOOK_NOT_VALID(hook2)) { 1248256550Smelifaro TOPOLOGY_WUNLOCK(); 1249231761Sglebius return (EINVAL); 1250231761Sglebius } 125170784Sjulian hook1->hk_peer->hk_peer = hook2->hk_peer; 125270784Sjulian hook2->hk_peer->hk_peer = hook1->hk_peer; 125352419Sjulian 125470939Sjulian hook1->hk_peer = &ng_deadhook; 125570939Sjulian hook2->hk_peer = &ng_deadhook; 1256256550Smelifaro TOPOLOGY_WUNLOCK(); 125770939Sjulian 1258163244Sglebius NG_HOOK_UNREF(hook1); 1259163244Sglebius NG_HOOK_UNREF(hook2); 1260163244Sglebius 126152419Sjulian /* XXX If we ever cache methods on hooks update them as well */ 126252419Sjulian ng_destroy_hook(hook1); 126352419Sjulian ng_destroy_hook(hook2); 126452419Sjulian return (0); 126552419Sjulian} 126652419Sjulian 126752419Sjulian/* 126852419Sjulian * Install a new netgraph type 126952419Sjulian */ 127052419Sjulianint 127152419Sjulianng_newtype(struct ng_type *tp) 127252419Sjulian{ 127352419Sjulian const size_t namelen = strlen(tp->name); 127452419Sjulian 127552419Sjulian /* Check version and type name fields */ 1276229003Sglebius if ((tp->version != NG_ABI_VERSION) || (namelen == 0) || 1277229003Sglebius (namelen >= NG_TYPESIZ)) { 127871047Sjulian TRAP_ERROR(); 1279131374Sjulian if (tp->version != NG_ABI_VERSION) { 1280229003Sglebius printf("Netgraph: Node type rejected. ABI mismatch. " 1281229003Sglebius "Suggest recompile\n"); 1282131374Sjulian } 128352419Sjulian return (EINVAL); 128452419Sjulian } 128552419Sjulian 128652419Sjulian /* Check for name collision */ 128752419Sjulian if (ng_findtype(tp->name) != NULL) { 128871047Sjulian TRAP_ERROR(); 128952419Sjulian return (EEXIST); 129052419Sjulian } 129152419Sjulian 129252419Sjulian /* Link in new type */ 1293230480Sglebius TYPELIST_WLOCK(); 129470700Sjulian LIST_INSERT_HEAD(&ng_typelist, tp, types); 129571603Sjulian tp->refs = 1; /* first ref is linked list */ 1296230480Sglebius TYPELIST_WUNLOCK(); 129752419Sjulian return (0); 129852419Sjulian} 129952419Sjulian 130052419Sjulian/* 130180222Sjulian * unlink a netgraph type 130280222Sjulian * If no examples exist 130380222Sjulian */ 130480222Sjulianint 130580222Sjulianng_rmtype(struct ng_type *tp) 130680222Sjulian{ 130780222Sjulian /* Check for name collision */ 130880222Sjulian if (tp->refs != 1) { 130980222Sjulian TRAP_ERROR(); 131080222Sjulian return (EBUSY); 131180222Sjulian } 131280222Sjulian 131380222Sjulian /* Unlink type */ 1314230480Sglebius TYPELIST_WLOCK(); 131580222Sjulian LIST_REMOVE(tp, types); 1316230480Sglebius TYPELIST_WUNLOCK(); 131780222Sjulian return (0); 131880222Sjulian} 131980222Sjulian 132080222Sjulian/* 132152419Sjulian * Look for a type of the name given 132252419Sjulian */ 132352419Sjulianstruct ng_type * 132452419Sjulianng_findtype(const char *typename) 132552419Sjulian{ 132652419Sjulian struct ng_type *type; 132752419Sjulian 1328230480Sglebius TYPELIST_RLOCK(); 132970700Sjulian LIST_FOREACH(type, &ng_typelist, types) { 133052419Sjulian if (strcmp(type->name, typename) == 0) 133152419Sjulian break; 133252419Sjulian } 1333230480Sglebius TYPELIST_RUNLOCK(); 133452419Sjulian return (type); 133552419Sjulian} 133652419Sjulian 133752419Sjulian/************************************************************************ 133852419Sjulian Composite routines 133952419Sjulian************************************************************************/ 134052419Sjulian/* 134171047Sjulian * Connect two nodes using the specified hooks, using queued functions. 134252419Sjulian */ 1343172806Smavstatic int 1344172806Smavng_con_part3(node_p node, item_p item, hook_p hook) 134552419Sjulian{ 1346172806Smav int error = 0; 134752419Sjulian 134871047Sjulian /* 134971047Sjulian * When we run, we know that the node 'node' is locked for us. 135071047Sjulian * Our caller has a reference on the hook. 135171047Sjulian * Our caller has a reference on the node. 135271047Sjulian * (In this case our caller is ng_apply_item() ). 135371047Sjulian * The peer hook has a reference on the hook. 135471849Sjulian * We are all set up except for the final call to the node, and 135571849Sjulian * the clearing of the INVALID flag. 135671047Sjulian */ 135771047Sjulian if (NG_HOOK_NODE(hook) == &ng_deadnode) { 135871047Sjulian /* 135971047Sjulian * The node must have been freed again since we last visited 136071047Sjulian * here. ng_destry_hook() has this effect but nothing else does. 136171047Sjulian * We should just release our references and 136271047Sjulian * free anything we can think of. 136371047Sjulian * Since we know it's been destroyed, and it's our caller 136471047Sjulian * that holds the references, just return. 136571047Sjulian */ 1366172806Smav ERROUT(ENOENT); 136752419Sjulian } 136871047Sjulian if (hook->hk_node->nd_type->connect) { 1369172806Smav if ((error = (*hook->hk_node->nd_type->connect) (hook))) { 137071047Sjulian ng_destroy_hook(hook); /* also zaps peer */ 137171849Sjulian printf("failed in ng_con_part3()\n"); 1372172806Smav ERROUT(error); 137371047Sjulian } 137452419Sjulian } 137571047Sjulian /* 137671047Sjulian * XXX this is wrong for SMP. Possibly we need 137771047Sjulian * to separate out 'create' and 'invalid' flags. 137871047Sjulian * should only set flags on hooks we have locked under our node. 137971047Sjulian */ 138071047Sjulian hook->hk_flags &= ~HK_INVALID; 1381172806Smavdone: 1382172806Smav NG_FREE_ITEM(item); 1383172806Smav return (error); 138471047Sjulian} 138552419Sjulian 1386172806Smavstatic int 1387172806Smavng_con_part2(node_p node, item_p item, hook_p hook) 138871047Sjulian{ 1389172806Smav hook_p peer; 1390172806Smav int error = 0; 139171047Sjulian 139252419Sjulian /* 139371047Sjulian * When we run, we know that the node 'node' is locked for us. 139471047Sjulian * Our caller has a reference on the hook. 139571047Sjulian * Our caller has a reference on the node. 139671047Sjulian * (In this case our caller is ng_apply_item() ). 139771047Sjulian * The peer hook has a reference on the hook. 139871047Sjulian * our node pointer points to the 'dead' node. 139971047Sjulian * First check the hook name is unique. 140071849Sjulian * Should not happen because we checked before queueing this. 140152419Sjulian */ 140271047Sjulian if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) { 140371047Sjulian TRAP_ERROR(); 140471047Sjulian ng_destroy_hook(hook); /* should destroy peer too */ 140571849Sjulian printf("failed in ng_con_part2()\n"); 1406172806Smav ERROUT(EEXIST); 140771047Sjulian } 140870939Sjulian /* 140971047Sjulian * Check if the node type code has something to say about it 141071047Sjulian * If it fails, the unref of the hook will also unref the attached node, 141171047Sjulian * however since that node is 'ng_deadnode' this will do nothing. 141271047Sjulian * The peer hook will also be destroyed. 141370939Sjulian */ 141471047Sjulian if (node->nd_type->newhook != NULL) { 1415172806Smav if ((error = (*node->nd_type->newhook)(node, hook, 1416172806Smav hook->hk_name))) { 141771047Sjulian ng_destroy_hook(hook); /* should destroy peer too */ 141871849Sjulian printf("failed in ng_con_part2()\n"); 1419172806Smav ERROUT(error); 142071047Sjulian } 142171047Sjulian } 142271047Sjulian 142371047Sjulian /* 142471047Sjulian * The 'type' agrees so far, so go ahead and link it in. 142571047Sjulian * We'll ask again later when we actually connect the hooks. 142671047Sjulian */ 142771047Sjulian hook->hk_node = node; /* just overwrite ng_deadnode */ 142871047Sjulian NG_NODE_REF(node); /* each hook counts as a reference */ 142971047Sjulian LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 143071047Sjulian node->nd_numhooks++; 143171047Sjulian NG_HOOK_REF(hook); /* one for the node */ 143271047Sjulian 143371047Sjulian /* 1434167677Srwatson * We now have a symmetrical situation, where both hooks have been 143574078Sjulian * linked to their nodes, the newhook methods have been called 143671047Sjulian * And the references are all correct. The hooks are still marked 143771047Sjulian * as invalid, as we have not called the 'connect' methods 143871047Sjulian * yet. 1439167677Srwatson * We can call the local one immediately as we have the 144071047Sjulian * node locked, but we need to queue the remote one. 144171047Sjulian */ 144271047Sjulian if (hook->hk_node->nd_type->connect) { 1443172806Smav if ((error = (*hook->hk_node->nd_type->connect) (hook))) { 144471047Sjulian ng_destroy_hook(hook); /* also zaps peer */ 144571849Sjulian printf("failed in ng_con_part2(A)\n"); 1446172806Smav ERROUT(error); 144771047Sjulian } 144871047Sjulian } 1449151974Sglebius 1450151974Sglebius /* 1451151974Sglebius * Acquire topo mutex to avoid race with ng_destroy_hook(). 1452151974Sglebius */ 1453256550Smelifaro TOPOLOGY_RLOCK(); 1454151974Sglebius peer = hook->hk_peer; 1455151974Sglebius if (peer == &ng_deadhook) { 1456256550Smelifaro TOPOLOGY_RUNLOCK(); 1457151974Sglebius printf("failed in ng_con_part2(B)\n"); 1458151974Sglebius ng_destroy_hook(hook); 1459172806Smav ERROUT(ENOENT); 1460151974Sglebius } 1461256550Smelifaro TOPOLOGY_RUNLOCK(); 1462151974Sglebius 1463173605Sglebius if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3, 1464173605Sglebius NULL, 0, NG_REUSE_ITEM))) { 1465151974Sglebius printf("failed in ng_con_part2(C)\n"); 146671849Sjulian ng_destroy_hook(hook); /* also zaps peer */ 1467172806Smav return (error); /* item was consumed. */ 146871849Sjulian } 146971047Sjulian hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */ 1470172806Smav return (0); /* item was consumed. */ 1471172806Smavdone: 1472172806Smav NG_FREE_ITEM(item); 1473172806Smav return (error); 147452419Sjulian} 147552419Sjulian 147652419Sjulian/* 1477152451Sglebius * Connect this node with another node. We assume that this node is 147871047Sjulian * currently locked, as we are only called from an NGM_CONNECT message. 147952419Sjulian */ 148071047Sjulianstatic int 1481172806Smavng_con_nodes(item_p item, node_p node, const char *name, 1482172806Smav node_p node2, const char *name2) 148352419Sjulian{ 1484152451Sglebius int error; 1485152451Sglebius hook_p hook; 1486152451Sglebius hook_p hook2; 148752419Sjulian 148871849Sjulian if (ng_findhook(node2, name2) != NULL) { 148971849Sjulian return(EEXIST); 149071849Sjulian } 149170939Sjulian if ((error = ng_add_hook(node, name, &hook))) /* gives us a ref */ 149252419Sjulian return (error); 149371047Sjulian /* Allocate the other hook and link it up */ 149471047Sjulian NG_ALLOC_HOOK(hook2); 1495140737Sglebius if (hook2 == NULL) { 149671047Sjulian TRAP_ERROR(); 149771047Sjulian ng_destroy_hook(hook); /* XXX check ref counts so far */ 149871047Sjulian NG_HOOK_UNREF(hook); /* including our ref */ 149971047Sjulian return (ENOMEM); 150071047Sjulian } 150171047Sjulian hook2->hk_refs = 1; /* start with a reference for us. */ 150271047Sjulian hook2->hk_flags = HK_INVALID; 150371047Sjulian hook2->hk_peer = hook; /* Link the two together */ 150471047Sjulian hook->hk_peer = hook2; 150571047Sjulian NG_HOOK_REF(hook); /* Add a ref for the peer to each*/ 150671047Sjulian NG_HOOK_REF(hook2); 1507152451Sglebius hook2->hk_node = &ng_deadnode; 1508125028Sharti strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ); 150971047Sjulian 151071047Sjulian /* 151171047Sjulian * Queue the function above. 151271047Sjulian * Procesing continues in that function in the lock context of 151371047Sjulian * the other node. 151471047Sjulian */ 1515173605Sglebius if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0, 1516173605Sglebius NG_NOFLAGS))) { 1517171885Smav printf("failed in ng_con_nodes(): %d\n", error); 1518171885Smav ng_destroy_hook(hook); /* also zaps peer */ 1519171885Smav } 152071047Sjulian 152171047Sjulian NG_HOOK_UNREF(hook); /* Let each hook go if it wants to */ 152271047Sjulian NG_HOOK_UNREF(hook2); 1523171885Smav return (error); 152471047Sjulian} 152571047Sjulian 152671047Sjulian/* 152771047Sjulian * Make a peer and connect. 152871047Sjulian * We assume that the local node is locked. 152971047Sjulian * The new node probably doesn't need a lock until 153071047Sjulian * it has a hook, because it cannot really have any work until then, 153171047Sjulian * but we should think about it a bit more. 153271047Sjulian * 153371047Sjulian * The problem may come if the other node also fires up 153471047Sjulian * some hardware or a timer or some other source of activation, 153571047Sjulian * also it may already get a command msg via it's ID. 153671047Sjulian * 153771047Sjulian * We could use the same method as ng_con_nodes() but we'd have 1538152451Sglebius * to add ability to remove the node when failing. (Not hard, just 153971047Sjulian * make arg1 point to the node to remove). 154071047Sjulian * Unless of course we just ignore failure to connect and leave 154171047Sjulian * an unconnected node? 154271047Sjulian */ 154371047Sjulianstatic int 154471047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type) 154571047Sjulian{ 1546152451Sglebius node_p node2; 1547152451Sglebius hook_p hook1, hook2; 1548152451Sglebius int error; 154971047Sjulian 155071047Sjulian if ((error = ng_make_node(type, &node2))) { 155152419Sjulian return (error); 155252419Sjulian } 155370939Sjulian 155471047Sjulian if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */ 155571849Sjulian ng_rmnode(node2, NULL, NULL, 0); 155671047Sjulian return (error); 155771047Sjulian } 155871047Sjulian 155971047Sjulian if ((error = ng_add_hook(node2, name2, &hook2))) { 156071849Sjulian ng_rmnode(node2, NULL, NULL, 0); 156171047Sjulian ng_destroy_hook(hook1); 156271047Sjulian NG_HOOK_UNREF(hook1); 156371047Sjulian return (error); 156471047Sjulian } 156571047Sjulian 156670939Sjulian /* 156771047Sjulian * Actually link the two hooks together. 156871047Sjulian */ 156971047Sjulian hook1->hk_peer = hook2; 157071047Sjulian hook2->hk_peer = hook1; 157171047Sjulian 157271047Sjulian /* Each hook is referenced by the other */ 157371047Sjulian NG_HOOK_REF(hook1); 157471047Sjulian NG_HOOK_REF(hook2); 157571047Sjulian 157671047Sjulian /* Give each node the opportunity to veto the pending connection */ 157771047Sjulian if (hook1->hk_node->nd_type->connect) { 157871047Sjulian error = (*hook1->hk_node->nd_type->connect) (hook1); 157971047Sjulian } 158071047Sjulian 158171047Sjulian if ((error == 0) && hook2->hk_node->nd_type->connect) { 158271047Sjulian error = (*hook2->hk_node->nd_type->connect) (hook2); 158371047Sjulian 158471047Sjulian } 158571047Sjulian 158671047Sjulian /* 158770939Sjulian * drop the references we were holding on the two hooks. 158870939Sjulian */ 158971047Sjulian if (error) { 159071047Sjulian ng_destroy_hook(hook2); /* also zaps hook1 */ 159171849Sjulian ng_rmnode(node2, NULL, NULL, 0); 159271047Sjulian } else { 159371047Sjulian /* As a last act, allow the hooks to be used */ 159471047Sjulian hook1->hk_flags &= ~HK_INVALID; 159571047Sjulian hook2->hk_flags &= ~HK_INVALID; 159671047Sjulian } 159771047Sjulian NG_HOOK_UNREF(hook1); 159870939Sjulian NG_HOOK_UNREF(hook2); 159970939Sjulian return (error); 160052419Sjulian} 160171047Sjulian 160270700Sjulian/************************************************************************ 160370700Sjulian Utility routines to send self messages 160470700Sjulian************************************************************************/ 160570700Sjulian 160671849Sjulian/* Shut this node down as soon as everyone is clear of it */ 1607167677Srwatson/* Should add arg "immediately" to jump the queue */ 160870700Sjulianint 1609186060Smavng_rmnode_self(node_p node) 161070700Sjulian{ 161171849Sjulian int error; 161252419Sjulian 161371849Sjulian if (node == &ng_deadnode) 161471849Sjulian return (0); 1615132464Sjulian node->nd_flags |= NGF_INVALID; 1616132464Sjulian if (node->nd_flags & NGF_CLOSING) 161771849Sjulian return (0); 161870700Sjulian 1619186060Smav error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0); 162071849Sjulian return (error); 162170700Sjulian} 162270700Sjulian 162371849Sjulianstatic void 162471047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2) 162571047Sjulian{ 162671047Sjulian ng_destroy_hook(hook); 162771849Sjulian return ; 162871047Sjulian} 162971047Sjulian 163070935Sjulianint 163170935Sjulianng_rmhook_self(hook_p hook) 163270935Sjulian{ 163371047Sjulian int error; 163470935Sjulian node_p node = NG_HOOK_NODE(hook); 163570935Sjulian 163671047Sjulian if (node == &ng_deadnode) 163771047Sjulian return (0); 163871047Sjulian 163971047Sjulian error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0); 164071047Sjulian return (error); 164170935Sjulian} 164270935Sjulian 164370700Sjulian/*********************************************************************** 164452419Sjulian * Parse and verify a string of the form: <NODE:><PATH> 164552419Sjulian * 164652419Sjulian * Such a string can refer to a specific node or a specific hook 164752419Sjulian * on a specific node, depending on how you look at it. In the 164852419Sjulian * latter case, the PATH component must not end in a dot. 164952419Sjulian * 165052419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string 165152419Sjulian * of hook names separated by dots. This breaks out the original 165252419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp 165352419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to 165452419Sjulian * the final hook component of <PATH>, if any, otherwise NULL. 165552419Sjulian * 165652419Sjulian * This returns -1 if the path is malformed. The char ** are optional. 165770700Sjulian ***********************************************************************/ 165852419Sjulianint 165952419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 166052419Sjulian{ 1661152451Sglebius char *node, *path, *hook; 1662152451Sglebius int k; 166352419Sjulian 166452419Sjulian /* 166552419Sjulian * Extract absolute NODE, if any 166652419Sjulian */ 166752419Sjulian for (path = addr; *path && *path != ':'; path++); 166852419Sjulian if (*path) { 166952419Sjulian node = addr; /* Here's the NODE */ 167052419Sjulian *path++ = '\0'; /* Here's the PATH */ 167152419Sjulian 167252419Sjulian /* Node name must not be empty */ 167352419Sjulian if (!*node) 167452419Sjulian return -1; 167552419Sjulian 167652419Sjulian /* A name of "." is OK; otherwise '.' not allowed */ 167752419Sjulian if (strcmp(node, ".") != 0) { 167852419Sjulian for (k = 0; node[k]; k++) 167952419Sjulian if (node[k] == '.') 168052419Sjulian return -1; 168152419Sjulian } 168252419Sjulian } else { 168352419Sjulian node = NULL; /* No absolute NODE */ 168452419Sjulian path = addr; /* Here's the PATH */ 168552419Sjulian } 168652419Sjulian 168752419Sjulian /* Snoop for illegal characters in PATH */ 168852419Sjulian for (k = 0; path[k]; k++) 168952419Sjulian if (path[k] == ':') 169052419Sjulian return -1; 169152419Sjulian 169252419Sjulian /* Check for no repeated dots in PATH */ 169352419Sjulian for (k = 0; path[k]; k++) 169452419Sjulian if (path[k] == '.' && path[k + 1] == '.') 169552419Sjulian return -1; 169652419Sjulian 169752419Sjulian /* Remove extra (degenerate) dots from beginning or end of PATH */ 169852419Sjulian if (path[0] == '.') 169952419Sjulian path++; 170052419Sjulian if (*path && path[strlen(path) - 1] == '.') 170152419Sjulian path[strlen(path) - 1] = 0; 170252419Sjulian 170352419Sjulian /* If PATH has a dot, then we're not talking about a hook */ 170452419Sjulian if (*path) { 170552419Sjulian for (hook = path, k = 0; path[k]; k++) 170652419Sjulian if (path[k] == '.') { 170752419Sjulian hook = NULL; 170852419Sjulian break; 170952419Sjulian } 171052419Sjulian } else 171152419Sjulian path = hook = NULL; 171252419Sjulian 171352419Sjulian /* Done */ 171452419Sjulian if (nodep) 171552419Sjulian *nodep = node; 171652419Sjulian if (pathp) 171752419Sjulian *pathp = path; 171852419Sjulian if (hookp) 171952419Sjulian *hookp = hook; 172052419Sjulian return (0); 172152419Sjulian} 172252419Sjulian 172352419Sjulian/* 172452419Sjulian * Given a path, which may be absolute or relative, and a starting node, 172570700Sjulian * return the destination node. 172652419Sjulian */ 172752419Sjulianint 1728229003Sglebiusng_path2noderef(node_p here, const char *address, node_p *destp, 1729229003Sglebius hook_p *lasthook) 173052419Sjulian{ 1731125028Sharti char fullpath[NG_PATHSIZ]; 1732219827Sglebius char *nodename, *path; 173370700Sjulian node_p node, oldnode; 173452419Sjulian 173552419Sjulian /* Initialize */ 173670784Sjulian if (destp == NULL) { 173771047Sjulian TRAP_ERROR(); 173852419Sjulian return EINVAL; 173970784Sjulian } 174052419Sjulian *destp = NULL; 174152419Sjulian 174252419Sjulian /* Make a writable copy of address for ng_path_parse() */ 174352419Sjulian strncpy(fullpath, address, sizeof(fullpath) - 1); 174452419Sjulian fullpath[sizeof(fullpath) - 1] = '\0'; 174552419Sjulian 174652419Sjulian /* Parse out node and sequence of hooks */ 174752419Sjulian if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 174871047Sjulian TRAP_ERROR(); 174952419Sjulian return EINVAL; 175052419Sjulian } 175152419Sjulian 175270700Sjulian /* 175370700Sjulian * For an absolute address, jump to the starting node. 175470700Sjulian * Note that this holds a reference on the node for us. 175570700Sjulian * Don't forget to drop the reference if we don't need it. 175670700Sjulian */ 175752419Sjulian if (nodename) { 175870700Sjulian node = ng_name2noderef(here, nodename); 175952419Sjulian if (node == NULL) { 176071047Sjulian TRAP_ERROR(); 176152419Sjulian return (ENOENT); 176252419Sjulian } 176370700Sjulian } else { 176470700Sjulian if (here == NULL) { 176571047Sjulian TRAP_ERROR(); 176670700Sjulian return (EINVAL); 176770700Sjulian } 176852419Sjulian node = here; 176970784Sjulian NG_NODE_REF(node); 177070700Sjulian } 177152419Sjulian 1772219827Sglebius if (path == NULL) { 1773219827Sglebius if (lasthook != NULL) 1774219827Sglebius *lasthook = NULL; 1775219827Sglebius *destp = node; 1776219827Sglebius return (0); 1777219827Sglebius } 1778219827Sglebius 177970700Sjulian /* 1780152451Sglebius * Now follow the sequence of hooks 1781219827Sglebius * 1782219827Sglebius * XXXGL: The path may demolish as we go the sequence, but if 1783219827Sglebius * we hold the topology mutex at critical places, then, I hope, 1784219827Sglebius * we would always have valid pointers in hand, although the 1785219827Sglebius * path behind us may no longer exist. 178670700Sjulian */ 1787219827Sglebius for (;;) { 1788219827Sglebius hook_p hook; 178952419Sjulian char *segment; 179052419Sjulian 179152419Sjulian /* 179252419Sjulian * Break out the next path segment. Replace the dot we just 1793219827Sglebius * found with a NUL; "path" points to the next segment (or the 179452419Sjulian * NUL at the end). 179552419Sjulian */ 1796219827Sglebius for (segment = path; *path != '\0'; path++) { 1797219827Sglebius if (*path == '.') { 1798219827Sglebius *path++ = '\0'; 179952419Sjulian break; 180052419Sjulian } 180152419Sjulian } 180252419Sjulian 180352419Sjulian /* We have a segment, so look for a hook by that name */ 180454096Sarchie hook = ng_findhook(node, segment); 180552419Sjulian 1806256550Smelifaro TOPOLOGY_WLOCK(); 180752419Sjulian /* Can't get there from here... */ 1808229003Sglebius if (hook == NULL || NG_HOOK_PEER(hook) == NULL || 1809229003Sglebius NG_HOOK_NOT_VALID(hook) || 1810229003Sglebius NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) { 181171047Sjulian TRAP_ERROR(); 181270784Sjulian NG_NODE_UNREF(node); 1813256550Smelifaro TOPOLOGY_WUNLOCK(); 181452419Sjulian return (ENOENT); 181552419Sjulian } 181652419Sjulian 181770700Sjulian /* 1818152451Sglebius * Hop on over to the next node 181970700Sjulian * XXX 1820152451Sglebius * Big race conditions here as hooks and nodes go away 182170700Sjulian * *** Idea.. store an ng_ID_t in each hook and use that 182270700Sjulian * instead of the direct hook in this crawl? 182370700Sjulian */ 182470700Sjulian oldnode = node; 182570784Sjulian if ((node = NG_PEER_NODE(hook))) 182670784Sjulian NG_NODE_REF(node); /* XXX RACE */ 182770784Sjulian NG_NODE_UNREF(oldnode); /* XXX another race */ 182870784Sjulian if (NG_NODE_NOT_VALID(node)) { 182970784Sjulian NG_NODE_UNREF(node); /* XXX more races */ 1830256550Smelifaro TOPOLOGY_WUNLOCK(); 1831219827Sglebius TRAP_ERROR(); 1832219827Sglebius return (ENXIO); 183370700Sjulian } 183452419Sjulian 1835219827Sglebius if (*path == '\0') { 1836219827Sglebius if (lasthook != NULL) { 1837219827Sglebius if (hook != NULL) { 1838219827Sglebius *lasthook = NG_HOOK_PEER(hook); 1839219827Sglebius NG_HOOK_REF(*lasthook); 1840219827Sglebius } else 1841219827Sglebius *lasthook = NULL; 1842219827Sglebius } 1843256550Smelifaro TOPOLOGY_WUNLOCK(); 1844219827Sglebius *destp = node; 1845219827Sglebius return (0); 1846219827Sglebius } 1847256550Smelifaro TOPOLOGY_WUNLOCK(); 184852419Sjulian } 184952419Sjulian} 185052419Sjulian 185170700Sjulian/***************************************************************\ 185270700Sjulian* Input queue handling. 185370700Sjulian* All activities are submitted to the node via the input queue 185470700Sjulian* which implements a multiple-reader/single-writer gate. 1855167677Srwatson* Items which cannot be handled immediately are queued. 185670700Sjulian* 185770700Sjulian* read-write queue locking inline functions * 185870700Sjulian\***************************************************************/ 185970700Sjulian 1860178228Smavstatic __inline void ng_queue_rw(node_p node, item_p item, int rw); 1861178228Smavstatic __inline item_p ng_dequeue(node_p node, int *rw); 1862178228Smavstatic __inline item_p ng_acquire_read(node_p node, item_p item); 1863178228Smavstatic __inline item_p ng_acquire_write(node_p node, item_p item); 1864178228Smavstatic __inline void ng_leave_read(node_p node); 1865178228Smavstatic __inline void ng_leave_write(node_p node); 186670700Sjulian 186752419Sjulian/* 186870700Sjulian * Definition of the bits fields in the ng_queue flag word. 186970700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle 187070700Sjulian * with them. 187170700Sjulian * 187271902Sjulian * The ordering here may be important! don't shuffle these. 187352419Sjulian */ 187470700Sjulian/*- 187570700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet) 187670700Sjulian | 187770700Sjulian V 187870700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+ 1879151973Sglebius | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1880151973Sglebius | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A| 1881151973Sglebius | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W| 188270700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+ 1883151973Sglebius \___________________________ ____________________________/ | | 1884151973Sglebius V | | 1885151973Sglebius [active reader count] | | 188670700Sjulian | | 1887151973Sglebius Operation Pending -------------------------------+ | 188870700Sjulian | 1889151973Sglebius Active Writer ---------------------------------------+ 189071902Sjulian 1891178039SmavNode queue has such semantics: 1892178039Smav- All flags modifications are atomic. 1893178039Smav- Reader count can be incremented only if there is no writer or pending flags. 1894178039Smav As soon as this can't be done with single operation, it is implemented with 1895178039Smav spin loop and atomic_cmpset(). 1896178039Smav- Writer flag can be set only if there is no any bits set. 1897178039Smav It is implemented with atomic_cmpset(). 1898178039Smav- Pending flag can be set any time, but to avoid collision on queue processing 1899178039Smav all queue fields are protected by the mutex. 1900178039Smav- Queue processing thread reads queue holding the mutex, but releases it while 1901178039Smav processing. When queue is empty pending flag is removed. 1902178039Smav*/ 190371902Sjulian 1904151973Sglebius#define WRITER_ACTIVE 0x00000001 1905151973Sglebius#define OP_PENDING 0x00000002 1906151973Sglebius#define READER_INCREMENT 0x00000004 1907151973Sglebius#define READER_MASK 0xfffffffc /* Not valid if WRITER_ACTIVE is set */ 1908151973Sglebius#define SAFETY_BARRIER 0x00100000 /* 128K items queued should be enough */ 190971902Sjulian 191071902Sjulian/* Defines of more elaborate states on the queue */ 1911151973Sglebius/* Mask of bits a new read cares about */ 1912151973Sglebius#define NGQ_RMASK (WRITER_ACTIVE|OP_PENDING) 191371902Sjulian 1914151973Sglebius/* Mask of bits a new write cares about */ 191571902Sjulian#define NGQ_WMASK (NGQ_RMASK|READER_MASK) 191671902Sjulian 1917151973Sglebius/* Test to decide if there is something on the queue. */ 1918151973Sglebius#define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING) 191971902Sjulian 1920151973Sglebius/* How to decide what the next queued item is. */ 1921178228Smav#define HEAD_IS_READER(QP) NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue)) 1922178228Smav#define HEAD_IS_WRITER(QP) NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */ 1923151973Sglebius 1924151973Sglebius/* Read the status to decide if the next item on the queue can now run. */ 1925151973Sglebius#define QUEUED_READER_CAN_PROCEED(QP) \ 1926151973Sglebius (((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0) 1927151973Sglebius#define QUEUED_WRITER_CAN_PROCEED(QP) \ 1928151973Sglebius (((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0) 1929151973Sglebius 193071902Sjulian/* Is there a chance of getting ANY work off the queue? */ 1931151973Sglebius#define NEXT_QUEUED_ITEM_CAN_PROCEED(QP) \ 1932151973Sglebius ((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) : \ 1933177953Smav QUEUED_WRITER_CAN_PROCEED(QP)) 193471902Sjulian 1935151238Sglebius#define NGQRW_R 0 1936151238Sglebius#define NGQRW_W 1 1937151238Sglebius 1938178228Smav#define NGQ2_WORKQ 0x00000001 1939178228Smav 194070700Sjulian/* 194170700Sjulian * Taking into account the current state of the queue and node, possibly take 194270700Sjulian * the next entry off the queue and return it. Return NULL if there was 194370700Sjulian * nothing we could return, either because there really was nothing there, or 194470700Sjulian * because the node was in a state where it cannot yet process the next item 194570700Sjulian * on the queue. 194670700Sjulian */ 194770700Sjulianstatic __inline item_p 1948178228Smavng_dequeue(node_p node, int *rw) 194970700Sjulian{ 195070700Sjulian item_p item; 1951178228Smav struct ng_queue *ngq = &node->nd_input_queue; 195271902Sjulian 1953178039Smav /* This MUST be called with the mutex held. */ 1954139039Sglebius mtx_assert(&ngq->q_mtx, MA_OWNED); 1955178039Smav 1956178039Smav /* If there is nothing queued, then just return. */ 1957151973Sglebius if (!QUEUE_ACTIVE(ngq)) { 1958154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; " 1959154275Sglebius "queue flags 0x%lx", __func__, 1960178228Smav node->nd_ID, node, ngq->q_flags); 1961151973Sglebius return (NULL); 1962151973Sglebius } 1963139039Sglebius 1964151973Sglebius /* 1965151973Sglebius * From here, we can assume there is a head item. 1966151973Sglebius * We need to find out what it is and if it can be dequeued, given 1967151973Sglebius * the current state of the node. 1968151973Sglebius */ 1969151973Sglebius if (HEAD_IS_READER(ngq)) { 1970177953Smav while (1) { 1971177953Smav long t = ngq->q_flags; 1972177953Smav if (t & WRITER_ACTIVE) { 1973178039Smav /* There is writer, reader can't proceed. */ 1974229003Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) queued " 1975229003Sglebius "reader can't proceed; queue flags 0x%lx", 1976229003Sglebius __func__, node->nd_ID, node, t); 1977177953Smav return (NULL); 1978177953Smav } 1979178228Smav if (atomic_cmpset_acq_int(&ngq->q_flags, t, 1980177953Smav t + READER_INCREMENT)) 1981177953Smav break; 1982177953Smav cpu_spinwait(); 1983151973Sglebius } 1984178039Smav /* We have got reader lock for the node. */ 1985151238Sglebius *rw = NGQRW_R; 1986178228Smav } else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING, 1987177953Smav OP_PENDING + WRITER_ACTIVE)) { 1988178039Smav /* We have got writer lock for the node. */ 1989151238Sglebius *rw = NGQRW_W; 199070700Sjulian } else { 1991178039Smav /* There is somebody other, writer can't proceed. */ 1992229003Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer can't " 1993229003Sglebius "proceed; queue flags 0x%lx", __func__, node->nd_ID, node, 1994229003Sglebius ngq->q_flags); 1995151973Sglebius return (NULL); 199670700Sjulian } 199752419Sjulian 199870700Sjulian /* 199970700Sjulian * Now we dequeue the request (whatever it may be) and correct the 200070700Sjulian * pending flags and the next and last pointers. 200170700Sjulian */ 2002178228Smav item = STAILQ_FIRST(&ngq->queue); 2003178228Smav STAILQ_REMOVE_HEAD(&ngq->queue, el_next); 2004178228Smav if (STAILQ_EMPTY(&ngq->queue)) 2005178228Smav atomic_clear_int(&ngq->q_flags, OP_PENDING); 2006229003Sglebius CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; queue " 2007229003Sglebius "flags 0x%lx", __func__, node->nd_ID, node, item, *rw ? "WRITER" : 2008229003Sglebius "READER", ngq->q_flags); 200970700Sjulian return (item); 201070700Sjulian} 201152419Sjulian 201270700Sjulian/* 2013178039Smav * Queue a packet to be picked up later by someone else. 2014178039Smav * If the queue could be run now, add node to the queue handler's worklist. 201570700Sjulian */ 201670700Sjulianstatic __inline void 2017178228Smavng_queue_rw(node_p node, item_p item, int rw) 201870700Sjulian{ 2019178228Smav struct ng_queue *ngq = &node->nd_input_queue; 2020151973Sglebius if (rw == NGQRW_W) 2021151973Sglebius NGI_SET_WRITER(item); 2022151973Sglebius else 2023151973Sglebius NGI_SET_READER(item); 2024241009Srstone item->depth = 1; 2025177953Smav 2026177953Smav NG_QUEUE_LOCK(ngq); 2027177953Smav /* Set OP_PENDING flag and enqueue the item. */ 2028178228Smav atomic_set_int(&ngq->q_flags, OP_PENDING); 2029178228Smav STAILQ_INSERT_TAIL(&ngq->queue, item, el_next); 2030177953Smav 2031154275Sglebius CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__, 2032178228Smav node->nd_ID, node, item, rw ? "WRITER" : "READER" ); 2033229003Sglebius 203470700Sjulian /* 2035151973Sglebius * We can take the worklist lock with the node locked 2036151973Sglebius * BUT NOT THE REVERSE! 2037151973Sglebius */ 2038151973Sglebius if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2039178228Smav ng_worklist_add(node); 2040177953Smav NG_QUEUE_UNLOCK(ngq); 204170700Sjulian} 204252419Sjulian 2043178039Smav/* Acquire reader lock on node. If node is busy, queue the packet. */ 204470700Sjulianstatic __inline item_p 2045178228Smavng_acquire_read(node_p node, item_p item) 204652419Sjulian{ 2047178228Smav KASSERT(node != &ng_deadnode, 2048151974Sglebius ("%s: working on deadnode", __func__)); 204952419Sjulian 2050177953Smav /* Reader needs node without writer and pending items. */ 2051229003Sglebius for (;;) { 2052178228Smav long t = node->nd_input_queue.q_flags; 2053177953Smav if (t & NGQ_RMASK) 2054177953Smav break; /* Node is not ready for reader. */ 2055229003Sglebius if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, t, 2056229003Sglebius t + READER_INCREMENT)) { 2057177953Smav /* Successfully grabbed node */ 2058177953Smav CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", 2059178228Smav __func__, node->nd_ID, node, item); 2060177953Smav return (item); 2061177953Smav } 2062177953Smav cpu_spinwait(); 2063297793Spfg } 206470700Sjulian 2065177953Smav /* Queue the request for later. */ 2066178228Smav ng_queue_rw(node, item, NGQRW_R); 206770700Sjulian 2068148236Sglebius return (NULL); 206970700Sjulian} 207070700Sjulian 2071178039Smav/* Acquire writer lock on node. If node is busy, queue the packet. */ 207270700Sjulianstatic __inline item_p 2073178228Smavng_acquire_write(node_p node, item_p item) 207470700Sjulian{ 2075178228Smav KASSERT(node != &ng_deadnode, 2076151974Sglebius ("%s: working on deadnode", __func__)); 2077151974Sglebius 2078177953Smav /* Writer needs completely idle node. */ 2079229003Sglebius if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, 0, 2080229003Sglebius WRITER_ACTIVE)) { 2081177953Smav /* Successfully grabbed node */ 2082154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", 2083178228Smav __func__, node->nd_ID, node, item); 208470700Sjulian return (item); 208552419Sjulian } 208652419Sjulian 2087177953Smav /* Queue the request for later. */ 2088178228Smav ng_queue_rw(node, item, NGQRW_W); 208970700Sjulian 2090148236Sglebius return (NULL); 209170700Sjulian} 209270700Sjulian 2093167385Sjulian#if 0 2094167385Sjulianstatic __inline item_p 2095178228Smavng_upgrade_write(node_p node, item_p item) 2096167385Sjulian{ 2097178228Smav struct ng_queue *ngq = &node->nd_input_queue; 2098178228Smav KASSERT(node != &ng_deadnode, 2099167385Sjulian ("%s: working on deadnode", __func__)); 2100167385Sjulian 2101167385Sjulian NGI_SET_WRITER(item); 2102167385Sjulian 2103178228Smav NG_QUEUE_LOCK(ngq); 2104167385Sjulian 2105167385Sjulian /* 2106167385Sjulian * There will never be no readers as we are there ourselves. 2107167385Sjulian * Set the WRITER_ACTIVE flags ASAP to block out fast track readers. 2108167385Sjulian * The caller we are running from will call ng_leave_read() 2109167385Sjulian * soon, so we must account for that. We must leave again with the 2110167385Sjulian * READER lock. If we find other readers, then 2111167385Sjulian * queue the request for later. However "later" may be rignt now 2112167385Sjulian * if there are no readers. We don't really care if there are queued 2113167385Sjulian * items as we will bypass them anyhow. 2114167385Sjulian */ 2115178228Smav atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT); 2116178228Smav if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) { 2117178228Smav NG_QUEUE_UNLOCK(ngq); 2118167385Sjulian 2119167385Sjulian /* It's just us, act on the item. */ 2120167385Sjulian /* will NOT drop writer lock when done */ 2121167385Sjulian ng_apply_item(node, item, 0); 2122167385Sjulian 2123167385Sjulian /* 2124229003Sglebius * Having acted on the item, atomically 2125229003Sglebius * downgrade back to READER and finish up. 2126167385Sjulian */ 2127229003Sglebius atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE); 2128167385Sjulian 2129167385Sjulian /* Our caller will call ng_leave_read() */ 2130167385Sjulian return; 2131167385Sjulian } 2132167385Sjulian /* 2133167385Sjulian * It's not just us active, so queue us AT THE HEAD. 2134167385Sjulian * "Why?" I hear you ask. 2135167385Sjulian * Put us at the head of the queue as we've already been 2136167385Sjulian * through it once. If there is nothing else waiting, 2137167385Sjulian * set the correct flags. 2138167385Sjulian */ 2139178228Smav if (STAILQ_EMPTY(&ngq->queue)) { 2140167385Sjulian /* We've gone from, 0 to 1 item in the queue */ 2141178228Smav atomic_set_int(&ngq->q_flags, OP_PENDING); 2142167385Sjulian 2143167385Sjulian CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__, 2144178228Smav node->nd_ID, node); 2145167385Sjulian }; 2146178228Smav STAILQ_INSERT_HEAD(&ngq->queue, item, el_next); 2147178228Smav CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER", 2148178228Smav __func__, node->nd_ID, node, item ); 2149167385Sjulian 2150167385Sjulian /* Reverse what we did above. That downgrades us back to reader */ 2151178228Smav atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE); 2152177953Smav if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2153178228Smav ng_worklist_add(node); 2154178228Smav NG_QUEUE_UNLOCK(ngq); 2155167385Sjulian 2156167385Sjulian return; 2157167385Sjulian} 2158167385Sjulian#endif 2159167385Sjulian 2160178039Smav/* Release reader lock. */ 216170700Sjulianstatic __inline void 2162178228Smavng_leave_read(node_p node) 216370700Sjulian{ 2164178228Smav atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT); 216570700Sjulian} 216670700Sjulian 2167178039Smav/* Release writer lock. */ 216870700Sjulianstatic __inline void 2169178228Smavng_leave_write(node_p node) 217070700Sjulian{ 2171178228Smav atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE); 217270700Sjulian} 217370700Sjulian 2174178039Smav/* Purge node queue. Called on node shutdown. */ 217570700Sjulianstatic void 2176178228Smavng_flush_input_queue(node_p node) 217770700Sjulian{ 2178178228Smav struct ng_queue *ngq = &node->nd_input_queue; 217970700Sjulian item_p item; 2180151973Sglebius 2181168049Swkoszek NG_QUEUE_LOCK(ngq); 2182178228Smav while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) { 2183178228Smav STAILQ_REMOVE_HEAD(&ngq->queue, el_next); 2184178228Smav if (STAILQ_EMPTY(&ngq->queue)) 2185178228Smav atomic_clear_int(&ngq->q_flags, OP_PENDING); 2186168049Swkoszek NG_QUEUE_UNLOCK(ngq); 218770700Sjulian 2188151973Sglebius /* If the item is supplying a callback, call it with an error */ 2189177071Smav if (item->apply != NULL) { 2190177071Smav if (item->depth == 1) 2191177071Smav item->apply->error = ENOENT; 2192177071Smav if (refcount_release(&item->apply->refs)) { 2193177071Smav (*item->apply->apply)(item->apply->context, 2194177071Smav item->apply->error); 2195177071Smav } 2196151283Sglebius } 2197132369Sjulian NG_FREE_ITEM(item); 2198168049Swkoszek NG_QUEUE_LOCK(ngq); 219970700Sjulian } 2200168049Swkoszek NG_QUEUE_UNLOCK(ngq); 220170700Sjulian} 220270700Sjulian 220370700Sjulian/*********************************************************************** 220470700Sjulian* Externally visible method for sending or queueing messages or data. 220570700Sjulian***********************************************************************/ 220670700Sjulian 220770700Sjulian/* 220871849Sjulian * The module code should have filled out the item correctly by this stage: 220970700Sjulian * Common: 221070700Sjulian * reference to destination node. 221170700Sjulian * Reference to destination rcv hook if relevant. 2212172806Smav * apply pointer must be or NULL or reference valid struct ng_apply_info. 221370700Sjulian * Data: 221470700Sjulian * pointer to mbuf 221570700Sjulian * Control_Message: 221670700Sjulian * pointer to msg. 221770700Sjulian * ID of original sender node. (return address) 221871849Sjulian * Function: 221971849Sjulian * Function pointer 222071849Sjulian * void * argument 222171849Sjulian * integer argument 222270700Sjulian * 222370700Sjulian * The nodes have several routines and macros to help with this task: 222470700Sjulian */ 222570700Sjulian 222670700Sjulianint 2227146281Sglebiusng_snd_item(item_p item, int flags) 222870700Sjulian{ 2229172806Smav hook_p hook; 2230172806Smav node_p node; 2231146281Sglebius int queue, rw; 2232172806Smav struct ng_queue *ngq; 2233151256Sglebius int error = 0; 223470700Sjulian 2235176046Smav /* We are sending item, so it must be present! */ 2236176046Smav KASSERT(item != NULL, ("ng_snd_item: item is NULL")); 2237172806Smav 223870784Sjulian#ifdef NETGRAPH_DEBUG 2239152451Sglebius _ngi_check(item, __FILE__, __LINE__); 224070700Sjulian#endif 224170700Sjulian 2242176046Smav /* Item was sent once more, postpone apply() call. */ 2243172806Smav if (item->apply) 2244172806Smav refcount_acquire(&item->apply->refs); 2245146281Sglebius 2246172806Smav node = NGI_NODE(item); 2247176046Smav /* Node is never optional. */ 2248176046Smav KASSERT(node != NULL, ("ng_snd_item: node is NULL")); 2249172806Smav 2250175850Smav hook = NGI_HOOK(item); 2251176046Smav /* Valid hook and mbuf are mandatory for data. */ 2252176046Smav if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) { 2253176046Smav KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL")); 2254131123Sjulian if (NGI_M(item) == NULL) 2255172806Smav ERROUT(EINVAL); 225670700Sjulian CHECK_DATA_MBUF(NGI_M(item)); 225769922Sjulian } 2258149505Sglebius 225970700Sjulian /* 2260176046Smav * If the item or the node specifies single threading, force 2261176046Smav * writer semantics. Similarly, the node may say one hook always 2262176046Smav * produces writers. These are overrides. 226370700Sjulian */ 2264176567Smav if (((item->el_flags & NGQF_RW) == NGQF_WRITER) || 2265176046Smav (node->nd_flags & NGF_FORCE_WRITER) || 2266176046Smav (hook && (hook->hk_flags & HK_FORCE_WRITER))) { 2267176046Smav rw = NGQRW_W; 2268176046Smav } else { 2269176046Smav rw = NGQRW_R; 2270176046Smav } 2271149505Sglebius 2272175847Smav /* 2273194012Szec * If sender or receiver requests queued delivery, or call graph 2274194012Szec * loops back from outbound to inbound path, or stack usage 2275175847Smav * level is dangerous - enqueue message. 2276175847Smav */ 2277175847Smav if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) { 2278175847Smav queue = 1; 2279194012Szec } else if (hook && (hook->hk_flags & HK_TO_INBOUND) && 2280194012Szec curthread->td_ng_outbound) { 2281194012Szec queue = 1; 2282176046Smav } else { 2283176046Smav queue = 0; 2284175847Smav#ifdef GET_STACK_USAGE 2285175868Smav /* 2286175871Smarck * Most of netgraph nodes have small stack consumption and 2287176046Smav * for them 25% of free stack space is more than enough. 2288175868Smav * Nodes/hooks with higher stack usage should be marked as 2289175889Smarck * HI_STACK. For them 50% of stack will be guaranteed then. 2290176046Smav * XXX: Values 25% and 50% are completely empirical. 2291175868Smav */ 2292176046Smav size_t st, su, sl; 2293175847Smav GET_STACK_USAGE(st, su); 2294176046Smav sl = st - su; 2295229003Sglebius if ((sl * 4 < st) || ((sl * 2 < st) && 2296229003Sglebius ((node->nd_flags & NGF_HI_STACK) || (hook && 2297229003Sglebius (hook->hk_flags & HK_HI_STACK))))) 2298175847Smav queue = 1; 2299176046Smav#endif 2300175847Smav } 2301175847Smav 230270700Sjulian if (queue) { 230370700Sjulian /* Put it on the queue for that node*/ 2304178228Smav ng_queue_rw(node, item, rw); 2305176046Smav return ((flags & NG_PROGRESS) ? EINPROGRESS : 0); 230670700Sjulian } 230769922Sjulian 230870700Sjulian /* 230970700Sjulian * We already decided how we will be queueud or treated. 231070700Sjulian * Try get the appropriate operating permission. 231170700Sjulian */ 2312151256Sglebius if (rw == NGQRW_R) 2313178228Smav item = ng_acquire_read(node, item); 2314151256Sglebius else 2315178228Smav item = ng_acquire_write(node, item); 231652419Sjulian 2317176046Smav /* Item was queued while trying to get permission. */ 2318176046Smav if (item == NULL) 2319176046Smav return ((flags & NG_PROGRESS) ? EINPROGRESS : 0); 232052419Sjulian 232174078Sjulian NGI_GET_NODE(item, node); /* zaps stored node */ 232252419Sjulian 2323177071Smav item->depth++; 2324170180Sglebius error = ng_apply_item(node, item, rw); /* drops r/w lock when done */ 232574078Sjulian 2326177953Smav /* If something is waiting on queue and ready, schedule it. */ 2327178228Smav ngq = &node->nd_input_queue; 2328177953Smav if (QUEUE_ACTIVE(ngq)) { 2329177953Smav NG_QUEUE_LOCK(ngq); 2330177953Smav if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2331178228Smav ng_worklist_add(node); 2332177953Smav NG_QUEUE_UNLOCK(ngq); 2333177953Smav } 2334177953Smav 233552419Sjulian /* 2336177953Smav * Node may go away as soon as we remove the reference. 2337177953Smav * Whatever we do, DO NOT access the node again! 233874078Sjulian */ 2339177953Smav NG_NODE_UNREF(node); 234074078Sjulian 234171849Sjulian return (error); 2342172806Smav 2343172806Smavdone: 2344176046Smav /* If was not sent, apply callback here. */ 2345177071Smav if (item->apply != NULL) { 2346177071Smav if (item->depth == 0 && error != 0) 2347177071Smav item->apply->error = error; 2348177071Smav if (refcount_release(&item->apply->refs)) { 2349177071Smav (*item->apply->apply)(item->apply->context, 2350177071Smav item->apply->error); 2351177071Smav } 2352177071Smav } 2353175850Smav 2354172806Smav NG_FREE_ITEM(item); 2355172806Smav return (error); 235652419Sjulian} 235752419Sjulian 235852419Sjulian/* 235970700Sjulian * We have an item that was possibly queued somewhere. 236070700Sjulian * It should contain all the information needed 236170700Sjulian * to run it on the appropriate node/hook. 2362172806Smav * If there is apply pointer and we own the last reference, call apply(). 236352419Sjulian */ 2364170180Sglebiusstatic int 2365151238Sglebiusng_apply_item(node_p node, item_p item, int rw) 236652419Sjulian{ 236770700Sjulian hook_p hook; 236870700Sjulian ng_rcvdata_t *rcvdata; 236971885Sjulian ng_rcvmsg_t *rcvmsg; 2370172806Smav struct ng_apply_info *apply; 2371177071Smav int error = 0, depth; 237252419Sjulian 2373176046Smav /* Node and item are never optional. */ 2374176046Smav KASSERT(node != NULL, ("ng_apply_item: node is NULL")); 2375176046Smav KASSERT(item != NULL, ("ng_apply_item: item is NULL")); 2376176046Smav 237771849Sjulian NGI_GET_HOOK(item, hook); /* clears stored hook */ 237870784Sjulian#ifdef NETGRAPH_DEBUG 2379152451Sglebius _ngi_check(item, __FILE__, __LINE__); 238070700Sjulian#endif 2381147774Sglebius 2382172806Smav apply = item->apply; 2383177071Smav depth = item->depth; 2384147774Sglebius 238571047Sjulian switch (item->el_flags & NGQF_TYPE) { 238670700Sjulian case NGQF_DATA: 238770700Sjulian /* 238870700Sjulian * Check things are still ok as when we were queued. 238970700Sjulian */ 2390176046Smav KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL")); 2391176046Smav if (NG_HOOK_NOT_VALID(hook) || 2392176046Smav NG_NODE_NOT_VALID(node)) { 2393167402Sjulian error = EIO; 239470700Sjulian NG_FREE_ITEM(item); 239571885Sjulian break; 239670700Sjulian } 239771885Sjulian /* 239871885Sjulian * If no receive method, just silently drop it. 2399229003Sglebius * Give preference to the hook over-ride method. 240071885Sjulian */ 2401229003Sglebius if ((!(rcvdata = hook->hk_rcvdata)) && 2402229003Sglebius (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) { 240371885Sjulian error = 0; 240471885Sjulian NG_FREE_ITEM(item); 240571885Sjulian break; 240671885Sjulian } 2407167402Sjulian error = (*rcvdata)(hook, item); 240870700Sjulian break; 240970700Sjulian case NGQF_MESG: 2410175850Smav if (hook && NG_HOOK_NOT_VALID(hook)) { 2411175850Smav /* 2412175850Smav * The hook has been zapped then we can't use it. 2413175850Smav * Immediately drop its reference. 2414175850Smav * The message may not need it. 2415175850Smav */ 2416175850Smav NG_HOOK_UNREF(hook); 2417175850Smav hook = NULL; 241870700Sjulian } 241970700Sjulian /* 242070700Sjulian * Similarly, if the node is a zombie there is 242170700Sjulian * nothing we can do with it, drop everything. 242270700Sjulian */ 242370784Sjulian if (NG_NODE_NOT_VALID(node)) { 242471047Sjulian TRAP_ERROR(); 2425167402Sjulian error = EINVAL; 242670700Sjulian NG_FREE_ITEM(item); 2427175850Smav break; 242870700Sjulian } 2429175850Smav /* 2430175850Smav * Call the appropriate message handler for the object. 2431175850Smav * It is up to the message handler to free the message. 2432175850Smav * If it's a generic message, handle it generically, 2433175850Smav * otherwise call the type's message handler (if it exists). 2434175850Smav * XXX (race). Remember that a queued message may 2435175850Smav * reference a node or hook that has just been 2436175850Smav * invalidated. It will exist as the queue code 2437175850Smav * is holding a reference, but.. 2438175850Smav */ 2439175850Smav if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) && 2440175850Smav ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) { 2441175850Smav error = ng_generic_msg(node, item, hook); 2442175850Smav break; 2443175850Smav } 2444175850Smav if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) && 2445175850Smav (!(rcvmsg = node->nd_type->rcvmsg))) { 2446175850Smav TRAP_ERROR(); 2447175850Smav error = 0; 2448175850Smav NG_FREE_ITEM(item); 2449175850Smav break; 2450175850Smav } 2451175850Smav error = (*rcvmsg)(node, item, hook); 245270700Sjulian break; 245371047Sjulian case NGQF_FN: 2454173605Sglebius case NGQF_FN2: 245571047Sjulian /* 2456182995Smav * In the case of the shutdown message we allow it to hit 245771849Sjulian * even if the node is invalid. 245871047Sjulian */ 2459182995Smav if (NG_NODE_NOT_VALID(node) && 2460182995Smav NGI_FN(item) != &ng_rmnode) { 246171047Sjulian TRAP_ERROR(); 2462167402Sjulian error = EINVAL; 2463143384Sglebius NG_FREE_ITEM(item); 246471047Sjulian break; 246571047Sjulian } 2466182995Smav /* Same is about some internal functions and invalid hook. */ 2467182995Smav if (hook && NG_HOOK_NOT_VALID(hook) && 2468182995Smav NGI_FN2(item) != &ng_con_part2 && 2469182995Smav NGI_FN2(item) != &ng_con_part3 && 2470182995Smav NGI_FN(item) != &ng_rmhook_part2) { 2471182995Smav TRAP_ERROR(); 2472182995Smav error = EINVAL; 2473182995Smav NG_FREE_ITEM(item); 2474182995Smav break; 2475182995Smav } 2476182995Smav 2477173605Sglebius if ((item->el_flags & NGQF_TYPE) == NGQF_FN) { 2478173605Sglebius (*NGI_FN(item))(node, hook, NGI_ARG1(item), 2479173605Sglebius NGI_ARG2(item)); 2480172806Smav NG_FREE_ITEM(item); 2481173605Sglebius } else /* it is NGQF_FN2 */ 2482173605Sglebius error = (*NGI_FN2(item))(node, item, hook); 2483172806Smav break; 248470700Sjulian } 248570700Sjulian /* 248670700Sjulian * We held references on some of the resources 248770700Sjulian * that we took from the item. Now that we have 248870700Sjulian * finished doing everything, drop those references. 248970700Sjulian */ 2490175850Smav if (hook) 249170784Sjulian NG_HOOK_UNREF(hook); 249270700Sjulian 2493176046Smav if (rw == NGQRW_R) 2494178228Smav ng_leave_read(node); 2495176046Smav else 2496178228Smav ng_leave_write(node); 2497147774Sglebius 2498147774Sglebius /* Apply callback. */ 2499177071Smav if (apply != NULL) { 2500177071Smav if (depth == 1 && error != 0) 2501177071Smav apply->error = error; 2502177071Smav if (refcount_release(&apply->refs)) 2503177071Smav (*apply->apply)(apply->context, apply->error); 2504177071Smav } 2505147774Sglebius 2506170180Sglebius return (error); 250770700Sjulian} 250870700Sjulian 250970700Sjulian/*********************************************************************** 251070700Sjulian * Implement the 'generic' control messages 251170700Sjulian ***********************************************************************/ 251270700Sjulianstatic int 251370700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook) 251470700Sjulian{ 251570700Sjulian int error = 0; 251670700Sjulian struct ng_mesg *msg; 251770700Sjulian struct ng_mesg *resp = NULL; 251870700Sjulian 251970700Sjulian NGI_GET_MSG(item, msg); 252052419Sjulian if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 252171047Sjulian TRAP_ERROR(); 252270700Sjulian error = EINVAL; 252370700Sjulian goto out; 252452419Sjulian } 252552419Sjulian switch (msg->header.cmd) { 252652419Sjulian case NGM_SHUTDOWN: 252771849Sjulian ng_rmnode(here, NULL, NULL, 0); 252852419Sjulian break; 252952419Sjulian case NGM_MKPEER: 253052419Sjulian { 253152419Sjulian struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 253252419Sjulian 253352419Sjulian if (msg->header.arglen != sizeof(*mkp)) { 253471047Sjulian TRAP_ERROR(); 253570700Sjulian error = EINVAL; 253670700Sjulian break; 253752419Sjulian } 253852419Sjulian mkp->type[sizeof(mkp->type) - 1] = '\0'; 253952419Sjulian mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 254052419Sjulian mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 254152419Sjulian error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 254252419Sjulian break; 254352419Sjulian } 254452419Sjulian case NGM_CONNECT: 254552419Sjulian { 254652419Sjulian struct ngm_connect *const con = 254752419Sjulian (struct ngm_connect *) msg->data; 254852419Sjulian node_p node2; 254952419Sjulian 255052419Sjulian if (msg->header.arglen != sizeof(*con)) { 255171047Sjulian TRAP_ERROR(); 255270700Sjulian error = EINVAL; 255370700Sjulian break; 255452419Sjulian } 255552419Sjulian con->path[sizeof(con->path) - 1] = '\0'; 255652419Sjulian con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 255752419Sjulian con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 255870700Sjulian /* Don't forget we get a reference.. */ 255970700Sjulian error = ng_path2noderef(here, con->path, &node2, NULL); 256052419Sjulian if (error) 256152419Sjulian break; 2562172806Smav error = ng_con_nodes(item, here, con->ourhook, 2563172806Smav node2, con->peerhook); 256470784Sjulian NG_NODE_UNREF(node2); 256552419Sjulian break; 256652419Sjulian } 256752419Sjulian case NGM_NAME: 256852419Sjulian { 256952419Sjulian struct ngm_name *const nam = (struct ngm_name *) msg->data; 257052419Sjulian 257152419Sjulian if (msg->header.arglen != sizeof(*nam)) { 257271047Sjulian TRAP_ERROR(); 257370700Sjulian error = EINVAL; 257470700Sjulian break; 257552419Sjulian } 257652419Sjulian nam->name[sizeof(nam->name) - 1] = '\0'; 257752419Sjulian error = ng_name_node(here, nam->name); 257852419Sjulian break; 257952419Sjulian } 258052419Sjulian case NGM_RMHOOK: 258152419Sjulian { 258252419Sjulian struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 258352419Sjulian hook_p hook; 258452419Sjulian 258552419Sjulian if (msg->header.arglen != sizeof(*rmh)) { 258671047Sjulian TRAP_ERROR(); 258770700Sjulian error = EINVAL; 258870700Sjulian break; 258952419Sjulian } 259052419Sjulian rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 259154096Sarchie if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 259252419Sjulian ng_destroy_hook(hook); 259352419Sjulian break; 259452419Sjulian } 259552419Sjulian case NGM_NODEINFO: 259652419Sjulian { 259752419Sjulian struct nodeinfo *ni; 259852419Sjulian 259970700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT); 260052419Sjulian if (resp == NULL) { 260152419Sjulian error = ENOMEM; 260252419Sjulian break; 260352419Sjulian } 260452419Sjulian 260552419Sjulian /* Fill in node info */ 260670700Sjulian ni = (struct nodeinfo *) resp->data; 260770784Sjulian if (NG_NODE_HAS_NAME(here)) 2608125028Sharti strcpy(ni->name, NG_NODE_NAME(here)); 2609125028Sharti strcpy(ni->type, here->nd_type->name); 261052722Sjulian ni->id = ng_node2ID(here); 261170784Sjulian ni->hooks = here->nd_numhooks; 261252419Sjulian break; 261352419Sjulian } 261452419Sjulian case NGM_LISTHOOKS: 261552419Sjulian { 261670784Sjulian const int nhooks = here->nd_numhooks; 261752419Sjulian struct hooklist *hl; 261852419Sjulian struct nodeinfo *ni; 261952419Sjulian hook_p hook; 262052419Sjulian 262152419Sjulian /* Get response struct */ 2622229003Sglebius NG_MKRESPONSE(resp, msg, sizeof(*hl) + 2623229003Sglebius (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 262452419Sjulian if (resp == NULL) { 262552419Sjulian error = ENOMEM; 262652419Sjulian break; 262752419Sjulian } 262870700Sjulian hl = (struct hooklist *) resp->data; 262952419Sjulian ni = &hl->nodeinfo; 263052419Sjulian 263152419Sjulian /* Fill in node info */ 263270784Sjulian if (NG_NODE_HAS_NAME(here)) 2633125028Sharti strcpy(ni->name, NG_NODE_NAME(here)); 2634125028Sharti strcpy(ni->type, here->nd_type->name); 263552722Sjulian ni->id = ng_node2ID(here); 263652419Sjulian 263752419Sjulian /* Cycle through the linked list of hooks */ 263852419Sjulian ni->hooks = 0; 263970784Sjulian LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) { 264052419Sjulian struct linkinfo *const link = &hl->link[ni->hooks]; 264152419Sjulian 264252419Sjulian if (ni->hooks >= nhooks) { 264352419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 264487599Sobrien __func__, "hooks"); 264552419Sjulian break; 264652419Sjulian } 264770784Sjulian if (NG_HOOK_NOT_VALID(hook)) 264852419Sjulian continue; 2649125028Sharti strcpy(link->ourhook, NG_HOOK_NAME(hook)); 2650125028Sharti strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook)); 265170784Sjulian if (NG_PEER_NODE_NAME(hook)[0] != '\0') 2652125028Sharti strcpy(link->nodeinfo.name, 2653125028Sharti NG_PEER_NODE_NAME(hook)); 2654125028Sharti strcpy(link->nodeinfo.type, 2655125028Sharti NG_PEER_NODE(hook)->nd_type->name); 265670784Sjulian link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook)); 265770784Sjulian link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks; 265852419Sjulian ni->hooks++; 265952419Sjulian } 266052419Sjulian break; 266152419Sjulian } 266252419Sjulian 266352419Sjulian case NGM_LISTNODES: 266452419Sjulian { 266552419Sjulian struct namelist *nl; 266652419Sjulian node_p node; 2667231831Sglebius int i; 266852419Sjulian 2669231831Sglebius IDHASH_RLOCK(); 2670231831Sglebius /* Get response struct. */ 2671231831Sglebius NG_MKRESPONSE(resp, msg, sizeof(*nl) + 2672338333Smav (V_ng_nodes * sizeof(struct nodeinfo)), M_NOWAIT); 2673231831Sglebius if (resp == NULL) { 2674231831Sglebius IDHASH_RUNLOCK(); 2675231831Sglebius error = ENOMEM; 2676231831Sglebius break; 2677231831Sglebius } 2678231831Sglebius nl = (struct namelist *) resp->data; 2679231831Sglebius 2680231831Sglebius /* Cycle through the lists of nodes. */ 2681231831Sglebius nl->numnames = 0; 2682231831Sglebius for (i = 0; i <= V_ng_ID_hmask; i++) { 2683231831Sglebius LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) { 2684231831Sglebius struct nodeinfo *const np = 2685231831Sglebius &nl->nodeinfo[nl->numnames]; 2686231831Sglebius 2687231831Sglebius if (NG_NODE_NOT_VALID(node)) 2688231831Sglebius continue; 2689231831Sglebius if (NG_NODE_HAS_NAME(node)) 2690231831Sglebius strcpy(np->name, NG_NODE_NAME(node)); 2691231831Sglebius strcpy(np->type, node->nd_type->name); 2692231831Sglebius np->id = ng_node2ID(node); 2693231831Sglebius np->hooks = node->nd_numhooks; 2694231831Sglebius KASSERT(nl->numnames < V_ng_nodes, 2695231831Sglebius ("%s: no space", __func__)); 2696231831Sglebius nl->numnames++; 269770912Sjulian } 269852419Sjulian } 2699231831Sglebius IDHASH_RUNLOCK(); 2700231831Sglebius break; 2701231831Sglebius } 2702231831Sglebius case NGM_LISTNAMES: 2703231831Sglebius { 2704231831Sglebius struct namelist *nl; 2705231831Sglebius node_p node; 2706231831Sglebius int i; 270752419Sjulian 2708231831Sglebius NAMEHASH_RLOCK(); 2709231831Sglebius /* Get response struct. */ 2710229003Sglebius NG_MKRESPONSE(resp, msg, sizeof(*nl) + 2711231831Sglebius (V_ng_named_nodes * sizeof(struct nodeinfo)), M_NOWAIT); 271252419Sjulian if (resp == NULL) { 2713230480Sglebius NAMEHASH_RUNLOCK(); 271452419Sjulian error = ENOMEM; 271552419Sjulian break; 271652419Sjulian } 271770700Sjulian nl = (struct namelist *) resp->data; 271852419Sjulian 2719231831Sglebius /* Cycle through the lists of nodes. */ 272052419Sjulian nl->numnames = 0; 2721231831Sglebius for (i = 0; i <= V_ng_name_hmask; i++) { 2722181803Sbz LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) { 2723176802Smav struct nodeinfo *const np = 2724176802Smav &nl->nodeinfo[nl->numnames]; 272552419Sjulian 2726176802Smav if (NG_NODE_NOT_VALID(node)) 2727176802Smav continue; 2728231831Sglebius strcpy(np->name, NG_NODE_NAME(node)); 2729176802Smav strcpy(np->type, node->nd_type->name); 2730176802Smav np->id = ng_node2ID(node); 2731176802Smav np->hooks = node->nd_numhooks; 2732231831Sglebius KASSERT(nl->numnames < V_ng_named_nodes, 2733231831Sglebius ("%s: no space", __func__)); 2734176802Smav nl->numnames++; 273552419Sjulian } 273652419Sjulian } 2737230480Sglebius NAMEHASH_RUNLOCK(); 273852419Sjulian break; 273952419Sjulian } 274052419Sjulian 274152419Sjulian case NGM_LISTTYPES: 274252419Sjulian { 274352419Sjulian struct typelist *tl; 274452419Sjulian struct ng_type *type; 274552419Sjulian int num = 0; 274652419Sjulian 2747230480Sglebius TYPELIST_RLOCK(); 274852419Sjulian /* Count number of types */ 2749230480Sglebius LIST_FOREACH(type, &ng_typelist, types) 275052419Sjulian num++; 275152419Sjulian 275252419Sjulian /* Get response struct */ 2753229003Sglebius NG_MKRESPONSE(resp, msg, sizeof(*tl) + 2754229003Sglebius (num * sizeof(struct typeinfo)), M_NOWAIT); 275552419Sjulian if (resp == NULL) { 2756230480Sglebius TYPELIST_RUNLOCK(); 275752419Sjulian error = ENOMEM; 275852419Sjulian break; 275952419Sjulian } 276070700Sjulian tl = (struct typelist *) resp->data; 276152419Sjulian 276252419Sjulian /* Cycle through the linked list of types */ 276352419Sjulian tl->numtypes = 0; 276470700Sjulian LIST_FOREACH(type, &ng_typelist, types) { 276552419Sjulian struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 276652419Sjulian 2767125028Sharti strcpy(tp->type_name, type->name); 276871603Sjulian tp->numnodes = type->refs - 1; /* don't count list */ 2769230480Sglebius KASSERT(tl->numtypes < num, ("%s: no space", __func__)); 277052419Sjulian tl->numtypes++; 277152419Sjulian } 2772230480Sglebius TYPELIST_RUNLOCK(); 277352419Sjulian break; 277452419Sjulian } 277552419Sjulian 277653913Sarchie case NGM_BINARY2ASCII: 277753913Sarchie { 2778369764Sdonner int bufSize = 1024; 277953913Sarchie const struct ng_parse_type *argstype; 278053913Sarchie const struct ng_cmdlist *c; 278170700Sjulian struct ng_mesg *binary, *ascii; 278253913Sarchie 278353913Sarchie /* Data area must contain a valid netgraph message */ 278453913Sarchie binary = (struct ng_mesg *)msg->data; 2785152451Sglebius if (msg->header.arglen < sizeof(struct ng_mesg) || 2786152451Sglebius (msg->header.arglen - sizeof(struct ng_mesg) < 2787152451Sglebius binary->header.arglen)) { 278871047Sjulian TRAP_ERROR(); 278953913Sarchie error = EINVAL; 279053913Sarchie break; 279153913Sarchie } 2792369764Sdonnerretry_b2a: 279370700Sjulian /* Get a response message with lots of room */ 279470700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 279570159Sjulian if (resp == NULL) { 279653913Sarchie error = ENOMEM; 279753913Sarchie break; 279853913Sarchie } 279970700Sjulian ascii = (struct ng_mesg *)resp->data; 280053913Sarchie 280153913Sarchie /* Copy binary message header to response message payload */ 280253913Sarchie bcopy(binary, ascii, sizeof(*binary)); 280353913Sarchie 280453913Sarchie /* Find command by matching typecookie and command number */ 2805229003Sglebius for (c = here->nd_type->cmdlist; c != NULL && c->name != NULL; 2806229003Sglebius c++) { 2807229003Sglebius if (binary->header.typecookie == c->cookie && 2808229003Sglebius binary->header.cmd == c->cmd) 280953913Sarchie break; 281053913Sarchie } 281153913Sarchie if (c == NULL || c->name == NULL) { 281253913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 2813229003Sglebius if (binary->header.typecookie == c->cookie && 2814229003Sglebius binary->header.cmd == c->cmd) 281553913Sarchie break; 281653913Sarchie } 281753913Sarchie if (c->name == NULL) { 281870700Sjulian NG_FREE_MSG(resp); 281953913Sarchie error = ENOSYS; 282053913Sarchie break; 282153913Sarchie } 282253913Sarchie } 282353913Sarchie 282453913Sarchie /* Convert command name to ASCII */ 282553913Sarchie snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 282653913Sarchie "%s", c->name); 282753913Sarchie 282853913Sarchie /* Convert command arguments to ASCII */ 282953913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 283053913Sarchie c->respType : c->mesgType; 283170912Sjulian if (argstype == NULL) { 283253913Sarchie *ascii->data = '\0'; 283370912Sjulian } else { 2834369764Sdonner error = ng_unparse(argstype, (u_char *)binary->data, 2835369764Sdonner ascii->data, bufSize); 2836369764Sdonner if (error == ERANGE) { 283770700Sjulian NG_FREE_MSG(resp); 2838369764Sdonner bufSize *= 2; 2839369764Sdonner goto retry_b2a; 2840369764Sdonner } else if (error) { 2841369764Sdonner NG_FREE_MSG(resp); 284253913Sarchie break; 284353913Sarchie } 284453913Sarchie } 284553913Sarchie 284653913Sarchie /* Return the result as struct ng_mesg plus ASCII string */ 284753913Sarchie bufSize = strlen(ascii->data) + 1; 284853913Sarchie ascii->header.arglen = bufSize; 284970700Sjulian resp->header.arglen = sizeof(*ascii) + bufSize; 285053913Sarchie break; 285153913Sarchie } 285253913Sarchie 285353913Sarchie case NGM_ASCII2BINARY: 285453913Sarchie { 2855208036Szec int bufSize = 20 * 1024; /* XXX hard coded constant */ 285653913Sarchie const struct ng_cmdlist *c; 285753913Sarchie const struct ng_parse_type *argstype; 285870700Sjulian struct ng_mesg *ascii, *binary; 285959178Sarchie int off = 0; 286053913Sarchie 286153913Sarchie /* Data area must contain at least a struct ng_mesg + '\0' */ 286253913Sarchie ascii = (struct ng_mesg *)msg->data; 2863152451Sglebius if ((msg->header.arglen < sizeof(*ascii) + 1) || 2864152451Sglebius (ascii->header.arglen < 1) || 2865152451Sglebius (msg->header.arglen < sizeof(*ascii) + 2866152451Sglebius ascii->header.arglen)) { 286771047Sjulian TRAP_ERROR(); 286853913Sarchie error = EINVAL; 286953913Sarchie break; 287053913Sarchie } 287153913Sarchie ascii->data[ascii->header.arglen - 1] = '\0'; 287253913Sarchie 287370700Sjulian /* Get a response message with lots of room */ 287470700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 287570159Sjulian if (resp == NULL) { 287653913Sarchie error = ENOMEM; 287753913Sarchie break; 287853913Sarchie } 287970700Sjulian binary = (struct ng_mesg *)resp->data; 288053913Sarchie 288153913Sarchie /* Copy ASCII message header to response message payload */ 288253913Sarchie bcopy(ascii, binary, sizeof(*ascii)); 288353913Sarchie 288453913Sarchie /* Find command by matching ASCII command string */ 288570784Sjulian for (c = here->nd_type->cmdlist; 288653913Sarchie c != NULL && c->name != NULL; c++) { 288753913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 288853913Sarchie break; 288953913Sarchie } 289053913Sarchie if (c == NULL || c->name == NULL) { 289153913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 289253913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 289353913Sarchie break; 289453913Sarchie } 289553913Sarchie if (c->name == NULL) { 289670700Sjulian NG_FREE_MSG(resp); 289753913Sarchie error = ENOSYS; 289853913Sarchie break; 289953913Sarchie } 290053913Sarchie } 290153913Sarchie 290253913Sarchie /* Convert command name to binary */ 290353913Sarchie binary->header.cmd = c->cmd; 290453913Sarchie binary->header.typecookie = c->cookie; 290553913Sarchie 290653913Sarchie /* Convert command arguments to binary */ 290753913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 290853913Sarchie c->respType : c->mesgType; 290970912Sjulian if (argstype == NULL) { 291053913Sarchie bufSize = 0; 291170912Sjulian } else { 2912229003Sglebius if ((error = ng_parse(argstype, ascii->data, &off, 2913229003Sglebius (u_char *)binary->data, &bufSize)) != 0) { 291470700Sjulian NG_FREE_MSG(resp); 291553913Sarchie break; 291653913Sarchie } 291753913Sarchie } 291853913Sarchie 291953913Sarchie /* Return the result */ 292053913Sarchie binary->header.arglen = bufSize; 292170700Sjulian resp->header.arglen = sizeof(*binary) + bufSize; 292253913Sarchie break; 292353913Sarchie } 292453913Sarchie 292562471Sphk case NGM_TEXT_CONFIG: 292652419Sjulian case NGM_TEXT_STATUS: 292752419Sjulian /* 292852419Sjulian * This one is tricky as it passes the command down to the 292952419Sjulian * actual node, even though it is a generic type command. 293070700Sjulian * This means we must assume that the item/msg is already freed 293152419Sjulian * when control passes back to us. 293252419Sjulian */ 293370784Sjulian if (here->nd_type->rcvmsg != NULL) { 293470700Sjulian NGI_MSG(item) = msg; /* put it back as we found it */ 293570784Sjulian return((*here->nd_type->rcvmsg)(here, item, lasthook)); 293652419Sjulian } 293752419Sjulian /* Fall through if rcvmsg not supported */ 293852419Sjulian default: 293971047Sjulian TRAP_ERROR(); 294052419Sjulian error = EINVAL; 294152419Sjulian } 294270700Sjulian /* 294370700Sjulian * Sometimes a generic message may be statically allocated 2944229003Sglebius * to avoid problems with allocating when in tight memory situations. 294570700Sjulian * Don't free it if it is so. 2946298813Spfg * I break them apart here, because erros may cause a free if the item 294770700Sjulian * in which case we'd be doing it twice. 294870700Sjulian * they are kept together above, to simplify freeing. 294970700Sjulian */ 295070700Sjulianout: 295170700Sjulian NG_RESPOND_MSG(error, here, item, resp); 2952185179Smav NG_FREE_MSG(msg); 295352419Sjulian return (error); 295452419Sjulian} 295552419Sjulian 295652419Sjulian/************************************************************************ 2957146213Sglebius Queue element get/free routines 2958146213Sglebius************************************************************************/ 2959146213Sglebius 2960146213Sglebiusuma_zone_t ng_qzone; 2961178259Smavuma_zone_t ng_qdzone; 2962186093Smavstatic int numthreads = 0; /* number of queue threads */ 2963176849Smavstatic int maxalloc = 4096;/* limit the damage of a leak */ 2964278640Sglebiusstatic int maxdata = 4096; /* limit the damage of a DoS */ 2965146213Sglebius 2966186093SmavSYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads, 2967186093Smav 0, "Number of queue processing threads"); 2968146213SglebiusSYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc, 2969178259Smav 0, "Maximum number of non-data queue items to allocate"); 2970178259SmavSYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata, 2971178259Smav 0, "Maximum number of data queue items to allocate"); 2972146213Sglebius 2973146213Sglebius#ifdef NETGRAPH_DEBUG 2974146213Sglebiusstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist); 2975229003Sglebiusstatic int allocated; /* number of items malloc'd */ 2976146213Sglebius#endif 2977146213Sglebius 2978146213Sglebius/* 2979146213Sglebius * Get a queue entry. 2980146213Sglebius * This is usually called when a packet first enters netgraph. 2981146213Sglebius * By definition, this is usually from an interrupt, or from a user. 2982146213Sglebius * Users are not so important, but try be quick for the times that it's 2983146213Sglebius * an interrupt. 2984146213Sglebius */ 2985146213Sglebiusstatic __inline item_p 2986178259Smavng_alloc_item(int type, int flags) 2987146213Sglebius{ 2988178259Smav item_p item; 2989146213Sglebius 2990178259Smav KASSERT(((type & ~NGQF_TYPE) == 0), 2991178259Smav ("%s: incorrect item type: %d", __func__, type)); 2992146213Sglebius 2993229003Sglebius item = uma_zalloc((type == NGQF_DATA) ? ng_qdzone : ng_qzone, 2994178259Smav ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO); 2995146281Sglebius 2996178259Smav if (item) { 2997178259Smav item->el_flags = type; 2998146213Sglebius#ifdef NETGRAPH_DEBUG 2999178259Smav mtx_lock(&ngq_mtx); 3000178259Smav TAILQ_INSERT_TAIL(&ng_itemlist, item, all); 3001178259Smav allocated++; 3002178259Smav mtx_unlock(&ngq_mtx); 3003178259Smav#endif 3004146213Sglebius } 3005146213Sglebius 3006146213Sglebius return (item); 3007146213Sglebius} 3008146213Sglebius 3009146213Sglebius/* 3010146213Sglebius * Release a queue entry 3011146213Sglebius */ 3012146213Sglebiusvoid 3013146213Sglebiusng_free_item(item_p item) 3014146213Sglebius{ 3015146213Sglebius /* 3016146213Sglebius * The item may hold resources on it's own. We need to free 3017146213Sglebius * these before we can free the item. What they are depends upon 3018146213Sglebius * what kind of item it is. it is important that nodes zero 3019146213Sglebius * out pointers to resources that they remove from the item 3020146213Sglebius * or we release them again here. 3021146213Sglebius */ 3022146213Sglebius switch (item->el_flags & NGQF_TYPE) { 3023146213Sglebius case NGQF_DATA: 3024146213Sglebius /* If we have an mbuf still attached.. */ 3025146213Sglebius NG_FREE_M(_NGI_M(item)); 3026146213Sglebius break; 3027146213Sglebius case NGQF_MESG: 3028146213Sglebius _NGI_RETADDR(item) = 0; 3029146213Sglebius NG_FREE_MSG(_NGI_MSG(item)); 3030146213Sglebius break; 3031146213Sglebius case NGQF_FN: 3032172806Smav case NGQF_FN2: 3033146213Sglebius /* nothing to free really, */ 3034146213Sglebius _NGI_FN(item) = NULL; 3035146213Sglebius _NGI_ARG1(item) = NULL; 3036146213Sglebius _NGI_ARG2(item) = 0; 3037146213Sglebius break; 3038146213Sglebius } 3039146213Sglebius /* If we still have a node or hook referenced... */ 3040146213Sglebius _NGI_CLR_NODE(item); 3041146213Sglebius _NGI_CLR_HOOK(item); 3042146213Sglebius 3043146213Sglebius#ifdef NETGRAPH_DEBUG 3044146213Sglebius mtx_lock(&ngq_mtx); 3045146213Sglebius TAILQ_REMOVE(&ng_itemlist, item, all); 3046146213Sglebius allocated--; 3047146213Sglebius mtx_unlock(&ngq_mtx); 3048146213Sglebius#endif 3049229003Sglebius uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA) ? 3050229003Sglebius ng_qdzone : ng_qzone, item); 3051146213Sglebius} 3052146213Sglebius 3053178259Smav/* 3054178259Smav * Change type of the queue entry. 3055178259Smav * Possibly reallocates it from another UMA zone. 3056178259Smav */ 3057178259Smavstatic __inline item_p 3058178259Smavng_realloc_item(item_p pitem, int type, int flags) 3059178259Smav{ 3060178259Smav item_p item; 3061178259Smav int from, to; 3062178259Smav 3063178259Smav KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__)); 3064178259Smav KASSERT(((type & ~NGQF_TYPE) == 0), 3065178259Smav ("%s: incorrect item type: %d", __func__, type)); 3066178259Smav 3067178259Smav from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA); 3068178259Smav to = (type == NGQF_DATA); 3069178259Smav if (from != to) { 3070178259Smav /* If reallocation is required do it and copy item. */ 3071178259Smav if ((item = ng_alloc_item(type, flags)) == NULL) { 3072178259Smav ng_free_item(pitem); 3073178259Smav return (NULL); 3074178259Smav } 3075178259Smav *item = *pitem; 3076178259Smav ng_free_item(pitem); 3077178259Smav } else 3078178259Smav item = pitem; 3079178259Smav item->el_flags = (item->el_flags & ~NGQF_TYPE) | type; 3080178259Smav 3081178259Smav return (item); 3082178259Smav} 3083178259Smav 3084146213Sglebius/************************************************************************ 308552419Sjulian Module routines 308652419Sjulian************************************************************************/ 308752419Sjulian 308852419Sjulian/* 308952419Sjulian * Handle the loading/unloading of a netgraph node type module 309052419Sjulian */ 309152419Sjulianint 309252419Sjulianng_mod_event(module_t mod, int event, void *data) 309352419Sjulian{ 309452419Sjulian struct ng_type *const type = data; 3095229003Sglebius int error = 0; 309652419Sjulian 309752419Sjulian switch (event) { 309852419Sjulian case MOD_LOAD: 309952419Sjulian 310052419Sjulian /* Register new netgraph node type */ 3101229003Sglebius if ((error = ng_newtype(type)) != 0) 310252419Sjulian break; 310352419Sjulian 310452419Sjulian /* Call type specific code */ 310552419Sjulian if (type->mod_event != NULL) 310670700Sjulian if ((error = (*type->mod_event)(mod, event, data))) { 3107230480Sglebius TYPELIST_WLOCK(); 310871603Sjulian type->refs--; /* undo it */ 310952419Sjulian LIST_REMOVE(type, types); 3110230480Sglebius TYPELIST_WUNLOCK(); 311170700Sjulian } 311252419Sjulian break; 311352419Sjulian 311452419Sjulian case MOD_UNLOAD: 311571603Sjulian if (type->refs > 1) { /* make sure no nodes exist! */ 311652419Sjulian error = EBUSY; 311771603Sjulian } else { 3118229003Sglebius if (type->refs == 0) /* failed load, nothing to undo */ 311971603Sjulian break; 312052419Sjulian if (type->mod_event != NULL) { /* check with type */ 312152419Sjulian error = (*type->mod_event)(mod, event, data); 3122229003Sglebius if (error != 0) /* type refuses.. */ 312352419Sjulian break; 312452419Sjulian } 3125230480Sglebius TYPELIST_WLOCK(); 312652419Sjulian LIST_REMOVE(type, types); 3127230480Sglebius TYPELIST_WUNLOCK(); 312852419Sjulian } 312952419Sjulian break; 313052419Sjulian 313152419Sjulian default: 313252419Sjulian if (type->mod_event != NULL) 313352419Sjulian error = (*type->mod_event)(mod, event, data); 313452419Sjulian else 3135132199Sphk error = EOPNOTSUPP; /* XXX ? */ 313652419Sjulian break; 313752419Sjulian } 313852419Sjulian return (error); 313952419Sjulian} 314052419Sjulian 3141231831Sglebiusstatic void 3142231831Sglebiusvnet_netgraph_init(const void *unused __unused) 3143231831Sglebius{ 3144231831Sglebius 3145231831Sglebius /* We start with small hashes, but they can grow. */ 3146231831Sglebius V_ng_ID_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_ID_hmask); 3147231831Sglebius V_ng_name_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_name_hmask); 3148231831Sglebius} 3149231831SglebiusVNET_SYSINIT(vnet_netgraph_init, SI_SUB_NETGRAPH, SI_ORDER_FIRST, 3150231831Sglebius vnet_netgraph_init, NULL); 3151231831Sglebius 3152229003Sglebius#ifdef VIMAGE 3153195837Srwatsonstatic void 3154195837Srwatsonvnet_netgraph_uninit(const void *unused __unused) 3155193731Szec{ 3156207572Szec node_p node = NULL, last_killed = NULL; 3157207572Szec int i; 3158193731Szec 3159207572Szec do { 3160207572Szec /* Find a node to kill */ 3161231831Sglebius IDHASH_RLOCK(); 3162231831Sglebius for (i = 0; i <= V_ng_ID_hmask; i++) { 3163231831Sglebius LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) { 3164207572Szec if (node != &ng_deadnode) { 3165207572Szec NG_NODE_REF(node); 3166207572Szec break; 3167207572Szec } 3168207572Szec } 3169207572Szec if (node != NULL) 3170207572Szec break; 3171207572Szec } 3172231831Sglebius IDHASH_RUNLOCK(); 3173207572Szec 3174207572Szec /* Attempt to kill it only if it is a regular node */ 3175207572Szec if (node != NULL) { 3176207572Szec if (node == last_killed) { 3177207572Szec /* This should never happen */ 3178229003Sglebius printf("ng node %s needs NGF_REALLY_DIE\n", 3179229003Sglebius node->nd_name); 3180207572Szec if (node->nd_flags & NGF_REALLY_DIE) 3181207572Szec panic("ng node %s won't die", 3182207572Szec node->nd_name); 3183207572Szec node->nd_flags |= NGF_REALLY_DIE; 3184207572Szec } 3185193731Szec ng_rmnode(node, NULL, NULL, 0); 3186207572Szec NG_NODE_UNREF(node); 3187207572Szec last_killed = node; 3188193731Szec } 3189207572Szec } while (node != NULL); 3190231831Sglebius 3191231831Sglebius hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask); 3192231831Sglebius hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_ID_hmask); 3193193731Szec} 3194231830SglebiusVNET_SYSUNINIT(vnet_netgraph_uninit, SI_SUB_NETGRAPH, SI_ORDER_FIRST, 3195195837Srwatson vnet_netgraph_uninit, NULL); 3196193731Szec#endif /* VIMAGE */ 3197193731Szec 319852419Sjulian/* 319952419Sjulian * Handle loading and unloading for this code. 320052419Sjulian * The only thing we need to link into is the NETISR strucure. 320152419Sjulian */ 320252419Sjulianstatic int 320352419Sjulianngb_mod_event(module_t mod, int event, void *data) 320452419Sjulian{ 3205186093Smav struct proc *p; 3206186093Smav struct thread *td; 3207186093Smav int i, error = 0; 320852419Sjulian 320952419Sjulian switch (event) { 321052419Sjulian case MOD_LOAD: 3211146212Sglebius /* Initialize everything. */ 3212168049Swkoszek NG_WORKLIST_LOCK_INIT(); 3213230480Sglebius rw_init(&ng_typelist_lock, "netgraph types"); 3214230480Sglebius rw_init(&ng_idhash_lock, "netgraph idhash"); 3215230480Sglebius rw_init(&ng_namehash_lock, "netgraph namehash"); 3216256550Smelifaro rw_init(&ng_topo_lock, "netgraph topology mutex"); 3217146212Sglebius#ifdef NETGRAPH_DEBUG 3218176802Smav mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL, 3219176802Smav MTX_DEF); 3220146212Sglebius mtx_init(&ngq_mtx, "netgraph item list mutex", NULL, 3221123278Struckman MTX_DEF); 3222146212Sglebius#endif 3223146212Sglebius ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item), 3224231997Sglebius NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0); 3225146212Sglebius uma_zone_set_max(ng_qzone, maxalloc); 3226229003Sglebius ng_qdzone = uma_zcreate("NetGraph data items", 3227229003Sglebius sizeof(struct ng_item), NULL, NULL, NULL, NULL, 3228231997Sglebius UMA_ALIGN_CACHE, 0); 3229178259Smav uma_zone_set_max(ng_qdzone, maxdata); 3230186093Smav /* Autoconfigure number of threads. */ 3231186093Smav if (numthreads <= 0) 3232186093Smav numthreads = mp_ncpus; 3233186093Smav /* Create threads. */ 3234186093Smav p = NULL; /* start with no process */ 3235186093Smav for (i = 0; i < numthreads; i++) { 3236186093Smav if (kproc_kthread_add(ngthread, NULL, &p, &td, 3237186093Smav RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) { 3238186093Smav numthreads = i; 3239186093Smav break; 3240186093Smav } 3241186093Smav } 324252419Sjulian break; 324352419Sjulian case MOD_UNLOAD: 3244167677Srwatson /* You can't unload it because an interface may be using it. */ 324552419Sjulian error = EBUSY; 324652419Sjulian break; 324752419Sjulian default: 324852419Sjulian error = EOPNOTSUPP; 324952419Sjulian break; 325052419Sjulian } 325152419Sjulian return (error); 325252419Sjulian} 325352419Sjulian 325452419Sjulianstatic moduledata_t netgraph_mod = { 325552419Sjulian "netgraph", 325652419Sjulian ngb_mod_event, 325752419Sjulian (NULL) 325852419Sjulian}; 3259231830SglebiusDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_FIRST); 326072946SjulianSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family"); 3261273377ShselaskySYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_ABI_VERSION,""); 3262273377ShselaskySYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_VERSION, ""); 326352419Sjulian 326470784Sjulian#ifdef NETGRAPH_DEBUG 326570700Sjulianvoid 326670784Sjuliandumphook (hook_p hook, char *file, int line) 326770784Sjulian{ 326870784Sjulian printf("hook: name %s, %d refs, Last touched:\n", 326970784Sjulian _NG_HOOK_NAME(hook), hook->hk_refs); 327070784Sjulian printf(" Last active @ %s, line %d\n", 327170784Sjulian hook->lastfile, hook->lastline); 327270784Sjulian if (line) { 327370784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 3274226829Sglebius#ifdef KDB 3275226829Sglebius kdb_backtrace(); 3276226829Sglebius#endif 327770784Sjulian } 327870784Sjulian} 327970784Sjulian 328070784Sjulianvoid 328170784Sjuliandumpnode(node_p node, char *file, int line) 328270784Sjulian{ 328370784Sjulian printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n", 328471047Sjulian _NG_NODE_ID(node), node->nd_type->name, 328570784Sjulian node->nd_numhooks, node->nd_flags, 328670784Sjulian node->nd_refs, node->nd_name); 328770784Sjulian printf(" Last active @ %s, line %d\n", 328870784Sjulian node->lastfile, node->lastline); 328970784Sjulian if (line) { 329070784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 3291226829Sglebius#ifdef KDB 3292226829Sglebius kdb_backtrace(); 3293226829Sglebius#endif 329470784Sjulian } 329570784Sjulian} 329670784Sjulian 329770784Sjulianvoid 329870700Sjuliandumpitem(item_p item, char *file, int line) 329970700Sjulian{ 3300146212Sglebius printf(" ACTIVE item, last used at %s, line %d", 3301146212Sglebius item->lastfile, item->lastline); 3302146212Sglebius switch(item->el_flags & NGQF_TYPE) { 3303146212Sglebius case NGQF_DATA: 3304146212Sglebius printf(" - [data]\n"); 3305146212Sglebius break; 3306146212Sglebius case NGQF_MESG: 3307146212Sglebius printf(" - retaddr[%d]:\n", _NGI_RETADDR(item)); 3308146212Sglebius break; 3309146212Sglebius case NGQF_FN: 3310172820Sru printf(" - fn@%p (%p, %p, %p, %d (%x))\n", 3311172820Sru _NGI_FN(item), 3312172820Sru _NGI_NODE(item), 3313172820Sru _NGI_HOOK(item), 3314172820Sru item->body.fn.fn_arg1, 3315172820Sru item->body.fn.fn_arg2, 3316172820Sru item->body.fn.fn_arg2); 3317172820Sru break; 3318172806Smav case NGQF_FN2: 3319173110Smav printf(" - fn2@%p (%p, %p, %p, %d (%x))\n", 3320172820Sru _NGI_FN2(item), 3321149735Sglebius _NGI_NODE(item), 3322149735Sglebius _NGI_HOOK(item), 3323146212Sglebius item->body.fn.fn_arg1, 3324146212Sglebius item->body.fn.fn_arg2, 3325146212Sglebius item->body.fn.fn_arg2); 3326146212Sglebius break; 332752419Sjulian } 332870784Sjulian if (line) { 332970784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 3330149735Sglebius if (_NGI_NODE(item)) { 333170784Sjulian printf("node %p ([%x])\n", 3332149735Sglebius _NGI_NODE(item), ng_node2ID(_NGI_NODE(item))); 333370784Sjulian } 333470784Sjulian } 333570700Sjulian} 333652419Sjulian 333770784Sjulianstatic void 333870784Sjulianng_dumpitems(void) 333970784Sjulian{ 334070784Sjulian item_p item; 334170784Sjulian int i = 1; 334270784Sjulian TAILQ_FOREACH(item, &ng_itemlist, all) { 334370784Sjulian printf("[%d] ", i++); 334470784Sjulian dumpitem(item, NULL, 0); 334570784Sjulian } 334670784Sjulian} 334770784Sjulian 334870784Sjulianstatic void 334970784Sjulianng_dumpnodes(void) 335070784Sjulian{ 335170784Sjulian node_p node; 335270784Sjulian int i = 1; 3353131008Srwatson mtx_lock(&ng_nodelist_mtx); 335470784Sjulian SLIST_FOREACH(node, &ng_allnodes, nd_all) { 335570784Sjulian printf("[%d] ", i++); 335670784Sjulian dumpnode(node, NULL, 0); 335770784Sjulian } 3358131008Srwatson mtx_unlock(&ng_nodelist_mtx); 335970784Sjulian} 336070784Sjulian 336170784Sjulianstatic void 336270784Sjulianng_dumphooks(void) 336370784Sjulian{ 336470784Sjulian hook_p hook; 336570784Sjulian int i = 1; 3366131008Srwatson mtx_lock(&ng_nodelist_mtx); 336770784Sjulian SLIST_FOREACH(hook, &ng_allhooks, hk_all) { 336870784Sjulian printf("[%d] ", i++); 336970784Sjulian dumphook(hook, NULL, 0); 337070784Sjulian } 3371131008Srwatson mtx_unlock(&ng_nodelist_mtx); 337270784Sjulian} 337370784Sjulian 337470700Sjulianstatic int 337570700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS) 337670700Sjulian{ 337770700Sjulian int error; 337870700Sjulian int val; 337970700Sjulian int i; 338070700Sjulian 338170700Sjulian val = allocated; 338270700Sjulian i = 1; 3383170289Sdwmalone error = sysctl_handle_int(oidp, &val, 0, req); 338471047Sjulian if (error != 0 || req->newptr == NULL) 338571047Sjulian return (error); 338671047Sjulian if (val == 42) { 338770784Sjulian ng_dumpitems(); 338870784Sjulian ng_dumpnodes(); 338970784Sjulian ng_dumphooks(); 339070700Sjulian } 339171047Sjulian return (0); 339252419Sjulian} 339352419Sjulian 339471047SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW, 339571047Sjulian 0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items"); 339670784Sjulian#endif /* NETGRAPH_DEBUG */ 339770700Sjulian 339870700Sjulian/*********************************************************************** 339970700Sjulian* Worklist routines 340070700Sjulian**********************************************************************/ 340152419Sjulian/* 340270700Sjulian * Pick a node off the list of nodes with work, 3403186093Smav * try get an item to process off it. Remove the node from the list. 340452419Sjulian */ 340570700Sjulianstatic void 3406186093Smavngthread(void *arg) 340752419Sjulian{ 3408177953Smav for (;;) { 3409177953Smav node_p node; 341052419Sjulian 3411177953Smav /* Get node from the worklist. */ 3412168049Swkoszek NG_WORKLIST_LOCK(); 3413186093Smav while ((node = STAILQ_FIRST(&ng_worklist)) == NULL) 3414186093Smav NG_WORKLIST_SLEEP(); 3415178228Smav STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work); 3416168049Swkoszek NG_WORKLIST_UNLOCK(); 3417193731Szec CURVNET_SET(node->nd_vnet); 3418154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist", 3419154275Sglebius __func__, node->nd_ID, node); 342070700Sjulian /* 342170700Sjulian * We have the node. We also take over the reference 342270700Sjulian * that the list had on it. 342370700Sjulian * Now process as much as you can, until it won't 342470700Sjulian * let you have another item off the queue. 342570700Sjulian * All this time, keep the reference 342670700Sjulian * that lets us be sure that the node still exists. 342770700Sjulian * Let the reference go at the last minute. 342870700Sjulian */ 342970700Sjulian for (;;) { 3430177953Smav item_p item; 3431151238Sglebius int rw; 3432151238Sglebius 3433168049Swkoszek NG_QUEUE_LOCK(&node->nd_input_queue); 3434178228Smav item = ng_dequeue(node, &rw); 343570700Sjulian if (item == NULL) { 3436178228Smav node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ; 3437168049Swkoszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 343870700Sjulian break; /* go look for another node */ 343970700Sjulian } else { 3440168049Swkoszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 344174078Sjulian NGI_GET_NODE(item, node); /* zaps stored node */ 3442151238Sglebius ng_apply_item(node, item, rw); 344374078Sjulian NG_NODE_UNREF(node); 344470700Sjulian } 344570700Sjulian } 344673238Sjulian NG_NODE_UNREF(node); 3447193731Szec CURVNET_RESTORE(); 344852419Sjulian } 344970700Sjulian} 345069922Sjulian 345172979Sjulian/* 345272979Sjulian * XXX 345372979Sjulian * It's posible that a debugging NG_NODE_REF may need 345472979Sjulian * to be outside the mutex zone 345572979Sjulian */ 345670700Sjulianstatic void 3457177953Smavng_worklist_add(node_p node) 345870700Sjulian{ 3459148236Sglebius 3460148266Sglebius mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED); 3461148236Sglebius 3462178228Smav if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) { 346369922Sjulian /* 346470700Sjulian * If we are not already on the work queue, 346570700Sjulian * then put us on. 346669922Sjulian */ 3467178228Smav node->nd_input_queue.q_flags2 |= NGQ2_WORKQ; 3468229003Sglebius NG_NODE_REF(node); /* XXX safe in mutex? */ 3469168049Swkoszek NG_WORKLIST_LOCK(); 3470178228Smav STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work); 3471168049Swkoszek NG_WORKLIST_UNLOCK(); 3472154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__, 3473154275Sglebius node->nd_ID, node); 3474186093Smav NG_WORKLIST_WAKEUP(); 3475177953Smav } else { 3476154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist", 3477154275Sglebius __func__, node->nd_ID, node); 3478177953Smav } 347970700Sjulian} 348070700Sjulian 348170700Sjulian/*********************************************************************** 348270700Sjulian* Externally useable functions to set up a queue item ready for sending 348370700Sjulian***********************************************************************/ 348470700Sjulian 348570784Sjulian#ifdef NETGRAPH_DEBUG 348670784Sjulian#define ITEM_DEBUG_CHECKS \ 348770700Sjulian do { \ 348871849Sjulian if (NGI_NODE(item) ) { \ 348970700Sjulian printf("item already has node"); \ 3490174898Srwatson kdb_enter(KDB_WHY_NETGRAPH, "has node"); \ 349171849Sjulian NGI_CLR_NODE(item); \ 349270700Sjulian } \ 349371849Sjulian if (NGI_HOOK(item) ) { \ 349470700Sjulian printf("item already has hook"); \ 3495174898Srwatson kdb_enter(KDB_WHY_NETGRAPH, "has hook"); \ 349671849Sjulian NGI_CLR_HOOK(item); \ 349770700Sjulian } \ 349870700Sjulian } while (0) 349970700Sjulian#else 350070784Sjulian#define ITEM_DEBUG_CHECKS 350170700Sjulian#endif 350270700Sjulian 350370700Sjulian/* 3504131374Sjulian * Put mbuf into the item. 350570700Sjulian * Hook and node references will be removed when the item is dequeued. 350670700Sjulian * (or equivalent) 350770700Sjulian * (XXX) Unsafe because no reference held by peer on remote node. 350870700Sjulian * remote node might go away in this timescale. 350970700Sjulian * We know the hooks can't go away because that would require getting 351070700Sjulian * a writer item on both nodes and we must have at least a reader 3511151973Sglebius * here to be able to do this. 351270700Sjulian * Note that the hook loaded is the REMOTE hook. 351370700Sjulian * 351470700Sjulian * This is possibly in the critical path for new data. 351570700Sjulian */ 351670700Sjulianitem_p 3517146281Sglebiusng_package_data(struct mbuf *m, int flags) 351870700Sjulian{ 351970700Sjulian item_p item; 352070700Sjulian 3521178259Smav if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) { 3522176849Smav NG_FREE_M(m); 3523176849Smav return (NULL); 3524176849Smav } 352570784Sjulian ITEM_DEBUG_CHECKS; 3526178259Smav item->el_flags |= NGQF_READER; 352770700Sjulian NGI_M(item) = m; 352870700Sjulian return (item); 352970700Sjulian} 353070700Sjulian 353170700Sjulian/* 353270700Sjulian * Allocate a queue item and put items into it.. 353370700Sjulian * Evaluate the address as this will be needed to queue it and 353470700Sjulian * to work out what some of the fields should be. 353570700Sjulian * Hook and node references will be removed when the item is dequeued. 353670700Sjulian * (or equivalent) 353770700Sjulian */ 353870700Sjulianitem_p 3539146281Sglebiusng_package_msg(struct ng_mesg *msg, int flags) 354070700Sjulian{ 354170700Sjulian item_p item; 354270700Sjulian 3543178259Smav if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) { 354471849Sjulian NG_FREE_MSG(msg); 354570700Sjulian return (NULL); 354669922Sjulian } 354770784Sjulian ITEM_DEBUG_CHECKS; 3548149505Sglebius /* Messages items count as writers unless explicitly exempted. */ 3549149505Sglebius if (msg->header.cmd & NGM_READONLY) 3550178259Smav item->el_flags |= NGQF_READER; 3551149505Sglebius else 3552178259Smav item->el_flags |= NGQF_WRITER; 355370700Sjulian /* 355470700Sjulian * Set the current lasthook into the queue item 355570700Sjulian */ 355670700Sjulian NGI_MSG(item) = msg; 3557102244Sarchie NGI_RETADDR(item) = 0; 355870700Sjulian return (item); 355970700Sjulian} 356069922Sjulian 356171849Sjulian#define SET_RETADDR(item, here, retaddr) \ 356271047Sjulian do { /* Data or fn items don't have retaddrs */ \ 356371047Sjulian if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) { \ 356470700Sjulian if (retaddr) { \ 356570700Sjulian NGI_RETADDR(item) = retaddr; \ 356670700Sjulian } else { \ 356770700Sjulian /* \ 356870700Sjulian * The old return address should be ok. \ 356970700Sjulian * If there isn't one, use the address \ 357070700Sjulian * here. \ 357170700Sjulian */ \ 357270700Sjulian if (NGI_RETADDR(item) == 0) { \ 357370700Sjulian NGI_RETADDR(item) \ 357470700Sjulian = ng_node2ID(here); \ 357570700Sjulian } \ 357670700Sjulian } \ 357770700Sjulian } \ 357870700Sjulian } while (0) 357970700Sjulian 358070700Sjulianint 358170700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr) 358270700Sjulian{ 358371849Sjulian hook_p peer; 358471849Sjulian node_p peernode; 358570784Sjulian ITEM_DEBUG_CHECKS; 358670700Sjulian /* 358770700Sjulian * Quick sanity check.. 358870784Sjulian * Since a hook holds a reference on it's node, once we know 358970784Sjulian * that the peer is still connected (even if invalid,) we know 359070784Sjulian * that the peer node is present, though maybe invalid. 359170700Sjulian */ 3592256550Smelifaro TOPOLOGY_RLOCK(); 3593229003Sglebius if ((hook == NULL) || NG_HOOK_NOT_VALID(hook) || 3594178311Smav NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) || 3595178311Smav NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) { 359670700Sjulian NG_FREE_ITEM(item); 359771047Sjulian TRAP_ERROR(); 3598256550Smelifaro TOPOLOGY_RUNLOCK(); 359973083Sjulian return (ENETDOWN); 360052419Sjulian } 360152419Sjulian 360270700Sjulian /* 360370700Sjulian * Transfer our interest to the other (peer) end. 360470700Sjulian */ 360571849Sjulian NG_HOOK_REF(peer); 3606178311Smav NG_NODE_REF(peernode); 360771849Sjulian NGI_SET_HOOK(item, peer); 360871849Sjulian NGI_SET_NODE(item, peernode); 360979706Sjulian SET_RETADDR(item, here, retaddr); 3610219827Sglebius 3611256550Smelifaro TOPOLOGY_RUNLOCK(); 3612219827Sglebius 361370700Sjulian return (0); 361470700Sjulian} 361552419Sjulian 361670700Sjulianint 3617227130Sfjoeng_address_path(node_p here, item_p item, const char *address, ng_ID_t retaddr) 361870700Sjulian{ 3619152451Sglebius node_p dest = NULL; 362070700Sjulian hook_p hook = NULL; 3621152451Sglebius int error; 362270700Sjulian 362370784Sjulian ITEM_DEBUG_CHECKS; 362470700Sjulian /* 362570700Sjulian * Note that ng_path2noderef increments the reference count 362670700Sjulian * on the node for us if it finds one. So we don't have to. 362770700Sjulian */ 362870700Sjulian error = ng_path2noderef(here, address, &dest, &hook); 362970700Sjulian if (error) { 363070700Sjulian NG_FREE_ITEM(item); 363170784Sjulian return (error); 363252419Sjulian } 363371849Sjulian NGI_SET_NODE(item, dest); 3634219827Sglebius if (hook) 363571849Sjulian NGI_SET_HOOK(item, hook); 3636219827Sglebius 363771849Sjulian SET_RETADDR(item, here, retaddr); 363870700Sjulian return (0); 363970700Sjulian} 364052419Sjulian 364170700Sjulianint 364270700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr) 364370700Sjulian{ 364470700Sjulian node_p dest; 364570700Sjulian 364670784Sjulian ITEM_DEBUG_CHECKS; 364770700Sjulian /* 364870700Sjulian * Find the target node. 364970700Sjulian */ 365070700Sjulian dest = ng_ID2noderef(ID); /* GETS REFERENCE! */ 365170700Sjulian if (dest == NULL) { 365270700Sjulian NG_FREE_ITEM(item); 365371047Sjulian TRAP_ERROR(); 365470700Sjulian return(EINVAL); 365570700Sjulian } 365670700Sjulian /* Fill out the contents */ 365771849Sjulian NGI_SET_NODE(item, dest); 365871849Sjulian NGI_CLR_HOOK(item); 365971849Sjulian SET_RETADDR(item, here, retaddr); 366052419Sjulian return (0); 366152419Sjulian} 366252419Sjulian 366352419Sjulian/* 366470700Sjulian * special case to send a message to self (e.g. destroy node) 366570700Sjulian * Possibly indicate an arrival hook too. 366670700Sjulian * Useful for removing that hook :-) 366752419Sjulian */ 366870700Sjulianitem_p 366970700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg) 367052419Sjulian{ 367170700Sjulian item_p item; 367252419Sjulian 367370700Sjulian /* 367470700Sjulian * Find the target node. 367570700Sjulian * If there is a HOOK argument, then use that in preference 367670700Sjulian * to the address. 367770700Sjulian */ 3678178259Smav if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) { 367971849Sjulian NG_FREE_MSG(msg); 368070700Sjulian return (NULL); 368152419Sjulian } 368270700Sjulian 368370700Sjulian /* Fill out the contents */ 3684178259Smav item->el_flags |= NGQF_WRITER; 368570784Sjulian NG_NODE_REF(here); 368671849Sjulian NGI_SET_NODE(item, here); 368771849Sjulian if (hook) { 368870784Sjulian NG_HOOK_REF(hook); 368971849Sjulian NGI_SET_HOOK(item, hook); 369071849Sjulian } 369170700Sjulian NGI_MSG(item) = msg; 369270700Sjulian NGI_RETADDR(item) = ng_node2ID(here); 369370700Sjulian return (item); 369452419Sjulian} 369552419Sjulian 3696172806Smav/* 3697172806Smav * Send ng_item_fn function call to the specified node. 3698172806Smav */ 3699172806Smav 3700146281Sglebiusint 3701173605Sglebiusng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2) 370271047Sjulian{ 370371047Sjulian 3704173605Sglebius return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS); 370571047Sjulian} 370671047Sjulian 3707172806Smavint 3708173605Sglebiusng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2, 3709172806Smav int flags) 3710172806Smav{ 3711172806Smav item_p item; 3712172806Smav 3713178259Smav if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) { 3714172806Smav return (ENOMEM); 3715172806Smav } 3716178259Smav item->el_flags |= NGQF_WRITER; 3717172806Smav NG_NODE_REF(node); /* and one for the item */ 3718172806Smav NGI_SET_NODE(item, node); 3719172806Smav if (hook) { 3720172806Smav NG_HOOK_REF(hook); 3721172806Smav NGI_SET_HOOK(item, hook); 3722172806Smav } 3723173605Sglebius NGI_FN(item) = fn; 3724172806Smav NGI_ARG1(item) = arg1; 3725172806Smav NGI_ARG2(item) = arg2; 3726172806Smav return(ng_snd_item(item, flags)); 3727172806Smav} 3728172806Smav 3729172806Smav/* 3730173605Sglebius * Send ng_item_fn2 function call to the specified node. 3731173605Sglebius * 3732173605Sglebius * If an optional pitem parameter is supplied, its apply 3733173605Sglebius * callback will be copied to the new item. If also NG_REUSE_ITEM 3734173605Sglebius * flag is set, no new item will be allocated, but pitem will 3735173605Sglebius * be used. 3736172806Smav */ 3737172806Smavint 3738173605Sglebiusng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1, 3739173605Sglebius int arg2, int flags) 3740172806Smav{ 3741172806Smav item_p item; 3742172806Smav 3743173605Sglebius KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0), 3744173605Sglebius ("%s: NG_REUSE_ITEM but no pitem", __func__)); 3745172806Smav 3746173605Sglebius /* 3747173605Sglebius * Allocate a new item if no supplied or 3748173605Sglebius * if we can't use supplied one. 3749173605Sglebius */ 3750173605Sglebius if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) { 3751178259Smav if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL) 3752173605Sglebius return (ENOMEM); 3753178259Smav if (pitem != NULL) 3754178259Smav item->apply = pitem->apply; 3755176849Smav } else { 3756178259Smav if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL) 3757178259Smav return (ENOMEM); 3758176849Smav } 3759172806Smav 3760178259Smav item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER; 3761172806Smav NG_NODE_REF(node); /* and one for the item */ 3762172806Smav NGI_SET_NODE(item, node); 3763172806Smav if (hook) { 3764172806Smav NG_HOOK_REF(hook); 3765172806Smav NGI_SET_HOOK(item, hook); 3766172806Smav } 3767172806Smav NGI_FN2(item) = fn; 3768172806Smav NGI_ARG1(item) = arg1; 3769172806Smav NGI_ARG2(item) = arg2; 3770172806Smav return(ng_snd_item(item, flags)); 3771172806Smav} 3772172806Smav 3773172806Smav/* 377491711Sjulian * Official timeout routines for Netgraph nodes. 377591711Sjulian */ 377691711Sjulianstatic void 3777140852Sglebiusng_callout_trampoline(void *arg) 377891711Sjulian{ 377991711Sjulian item_p item = arg; 378091711Sjulian 3781193731Szec CURVNET_SET(NGI_NODE(item)->nd_vnet); 378291711Sjulian ng_snd_item(item, 0); 3783193731Szec CURVNET_RESTORE(); 378491711Sjulian} 378591711Sjulian 3786137138Sglebiusint 3787138268Sglebiusng_callout(struct callout *c, node_p node, hook_p hook, int ticks, 378891711Sjulian ng_item_fn *fn, void * arg1, int arg2) 378991711Sjulian{ 3790149881Sglebius item_p item, oitem; 379191711Sjulian 3792178259Smav if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL) 3793137138Sglebius return (ENOMEM); 3794137138Sglebius 3795178259Smav item->el_flags |= NGQF_WRITER; 379691711Sjulian NG_NODE_REF(node); /* and one for the item */ 379791711Sjulian NGI_SET_NODE(item, node); 379891711Sjulian if (hook) { 379991711Sjulian NG_HOOK_REF(hook); 380091711Sjulian NGI_SET_HOOK(item, hook); 380191711Sjulian } 380291711Sjulian NGI_FN(item) = fn; 380391711Sjulian NGI_ARG1(item) = arg1; 380491711Sjulian NGI_ARG2(item) = arg2; 3805149881Sglebius oitem = c->c_arg; 3806149881Sglebius if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 && 3807149881Sglebius oitem != NULL) 3808149881Sglebius NG_FREE_ITEM(oitem); 3809137138Sglebius return (0); 381091711Sjulian} 381191711Sjulian 381291711Sjulian/* A special modified version of untimeout() */ 3813152451Sglebiusint 3814138268Sglebiusng_uncallout(struct callout *c, node_p node) 381591711Sjulian{ 381691711Sjulian item_p item; 3817137138Sglebius int rval; 3818149357Sglebius 3819149357Sglebius KASSERT(c != NULL, ("ng_uncallout: NULL callout")); 3820149357Sglebius KASSERT(node != NULL, ("ng_uncallout: NULL node")); 3821149357Sglebius 3822137230Sglebius rval = callout_stop(c); 3823137138Sglebius item = c->c_arg; 3824137138Sglebius /* Do an extra check */ 3825140852Sglebius if ((rval > 0) && (c->c_func == &ng_callout_trampoline) && 3826333615Ssbruno (item != NULL) && (NGI_NODE(item) == node)) { 382791711Sjulian /* 382891711Sjulian * We successfully removed it from the queue before it ran 3829152451Sglebius * So now we need to unreference everything that was 383091711Sjulian * given extra references. (NG_FREE_ITEM does this). 383191711Sjulian */ 383291711Sjulian NG_FREE_ITEM(item); 383391711Sjulian } 3834149881Sglebius c->c_arg = NULL; 3835137138Sglebius 3836310248Shselasky /* 3837310248Shselasky * Callers only want to know if the callout was cancelled and 3838310248Shselasky * not draining or stopped. 3839310248Shselasky */ 3840310248Shselasky return (rval > 0); 384191711Sjulian} 384291711Sjulian 384370700Sjulian/* 384470700Sjulian * Set the address, if none given, give the node here. 384570700Sjulian */ 384670700Sjulianvoid 384770700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr) 384870700Sjulian{ 384970700Sjulian if (retaddr) { 385070700Sjulian NGI_RETADDR(item) = retaddr; 385170700Sjulian } else { 385270700Sjulian /* 385370700Sjulian * The old return address should be ok. 385470700Sjulian * If there isn't one, use the address here. 385570700Sjulian */ 385670700Sjulian NGI_RETADDR(item) = ng_node2ID(here); 385770700Sjulian } 385870700Sjulian} 3859