ng_base.c revision 338333
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 338333 2018-08-27 03:42:19Z mav $ 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 85752419Sjulian /* Check the name is valid */ 858125028Sharti for (i = 0; i < NG_NODESIZ; i++) { 85952419Sjulian if (name[i] == '\0' || name[i] == '.' || name[i] == ':') 86052419Sjulian break; 86152419Sjulian } 86252419Sjulian if (i == 0 || name[i] != '\0') { 86371047Sjulian TRAP_ERROR(); 86452419Sjulian return (EINVAL); 86552419Sjulian } 86652722Sjulian if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ 86771047Sjulian TRAP_ERROR(); 86852419Sjulian return (EINVAL); 86952419Sjulian } 87052419Sjulian 871231831Sglebius NAMEHASH_WLOCK(); 872231831Sglebius if (V_ng_named_nodes * 2 > V_ng_name_hmask) 873231831Sglebius ng_name_rehash(); 87452419Sjulian 875231831Sglebius hash = hash32_str(name, HASHINIT) & V_ng_name_hmask; 876231831Sglebius /* Check the name isn't already being used. */ 877231831Sglebius LIST_FOREACH(node2, &V_ng_name_hash[hash], nd_nodes) 878231831Sglebius if (NG_NODE_IS_VALID(node2) && 879231831Sglebius (strcmp(NG_NODE_NAME(node2), name) == 0)) { 880231831Sglebius NAMEHASH_WUNLOCK(); 881231831Sglebius return (EADDRINUSE); 882231831Sglebius } 883231831Sglebius 884231831Sglebius if (NG_NODE_HAS_NAME(node)) 885231831Sglebius LIST_REMOVE(node, nd_nodes); 886231831Sglebius else 887231831Sglebius V_ng_named_nodes++; 888231831Sglebius /* Copy it. */ 889125028Sharti strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ); 890176802Smav /* Update name hash. */ 891181803Sbz LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes); 892230480Sglebius NAMEHASH_WUNLOCK(); 893176802Smav 89452419Sjulian return (0); 89552419Sjulian} 89652419Sjulian 89752419Sjulian/* 89852419Sjulian * Find a node by absolute name. The name should NOT end with ':' 89952419Sjulian * The name "." means "this node" and "[xxx]" means "the node 90052419Sjulian * with ID (ie, at address) xxx". 90152419Sjulian * 90252419Sjulian * Returns the node if found, else NULL. 90370700Sjulian * Eventually should add something faster than a sequential search. 904170035Srwatson * Note it acquires a reference on the node so you can be sure it's still 905170035Srwatson * there. 90652419Sjulian */ 90752419Sjuliannode_p 90870700Sjulianng_name2noderef(node_p here, const char *name) 90952419Sjulian{ 91052722Sjulian node_p node; 91152722Sjulian ng_ID_t temp; 912176802Smav int hash; 91352419Sjulian 91452419Sjulian /* "." means "this node" */ 91570700Sjulian if (strcmp(name, ".") == 0) { 91670784Sjulian NG_NODE_REF(here); 91770700Sjulian return(here); 91870700Sjulian } 91952419Sjulian 92052419Sjulian /* Check for name-by-ID */ 92152722Sjulian if ((temp = ng_decodeidname(name)) != 0) { 92270700Sjulian return (ng_ID2noderef(temp)); 92352419Sjulian } 92452419Sjulian 925231831Sglebius /* Find node by name. */ 926231831Sglebius hash = hash32_str(name, HASHINIT) & V_ng_name_hmask; 927230480Sglebius NAMEHASH_RLOCK(); 928230480Sglebius LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes) 929176802Smav if (NG_NODE_IS_VALID(node) && 930176802Smav (strcmp(NG_NODE_NAME(node), name) == 0)) { 931230480Sglebius NG_NODE_REF(node); 93252419Sjulian break; 93370912Sjulian } 934230480Sglebius NAMEHASH_RUNLOCK(); 935230480Sglebius 93652419Sjulian return (node); 93752419Sjulian} 93852419Sjulian 93952419Sjulian/* 940108533Sschweikh * Decode an ID name, eg. "[f03034de]". Returns 0 if the 94152722Sjulian * string is not valid, otherwise returns the value. 94252419Sjulian */ 94352722Sjulianstatic ng_ID_t 94452419Sjulianng_decodeidname(const char *name) 94552419Sjulian{ 94652816Sarchie const int len = strlen(name); 94753648Sarchie char *eptr; 94852816Sarchie u_long val; 94952419Sjulian 95052816Sarchie /* Check for proper length, brackets, no leading junk */ 951229003Sglebius if ((len < 3) || (name[0] != '[') || (name[len - 1] != ']') || 952229003Sglebius (!isxdigit(name[1]))) 95370912Sjulian return ((ng_ID_t)0); 95452419Sjulian 95552816Sarchie /* Decode number */ 95652816Sarchie val = strtoul(name + 1, &eptr, 16); 957229003Sglebius if ((eptr - name != len - 1) || (val == ULONG_MAX) || (val == 0)) 95853042Sjulian return ((ng_ID_t)0); 959229003Sglebius 960229003Sglebius return ((ng_ID_t)val); 96152419Sjulian} 96252419Sjulian 96352419Sjulian/* 96452419Sjulian * Remove a name from a node. This should only be called 96552419Sjulian * when shutting down and removing the node. 96652419Sjulian */ 96752419Sjulianvoid 96852419Sjulianng_unname(node_p node) 96952419Sjulian{ 97052419Sjulian} 97152419Sjulian 972231831Sglebius/* 973231831Sglebius * Allocate a bigger name hash. 974231831Sglebius */ 975231831Sglebiusstatic void 976231831Sglebiusng_name_rehash() 977231831Sglebius{ 978231831Sglebius struct nodehash *new; 979231831Sglebius uint32_t hash; 980231831Sglebius u_long hmask; 981231831Sglebius node_p node, node2; 982231831Sglebius int i; 983231831Sglebius 984231831Sglebius new = hashinit_flags((V_ng_name_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask, 985231831Sglebius HASH_NOWAIT); 986231831Sglebius if (new == NULL) 987231831Sglebius return; 988231831Sglebius 989231831Sglebius for (i = 0; i <= V_ng_name_hmask; i++) 990231831Sglebius LIST_FOREACH_SAFE(node, &V_ng_name_hash[i], nd_nodes, node2) { 991231831Sglebius#ifdef INVARIANTS 992231831Sglebius LIST_REMOVE(node, nd_nodes); 993231831Sglebius#endif 994231831Sglebius hash = hash32_str(NG_NODE_NAME(node), HASHINIT) & hmask; 995231831Sglebius LIST_INSERT_HEAD(&new[hash], node, nd_nodes); 996231831Sglebius } 997231831Sglebius 998231831Sglebius hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask); 999231831Sglebius V_ng_name_hash = new; 1000231831Sglebius V_ng_name_hmask = hmask; 1001231831Sglebius} 1002231831Sglebius 1003231831Sglebius/* 1004231831Sglebius * Allocate a bigger ID hash. 1005231831Sglebius */ 1006231831Sglebiusstatic void 1007231831Sglebiusng_ID_rehash() 1008231831Sglebius{ 1009231831Sglebius struct nodehash *new; 1010231831Sglebius uint32_t hash; 1011231831Sglebius u_long hmask; 1012231831Sglebius node_p node, node2; 1013231831Sglebius int i; 1014231831Sglebius 1015231831Sglebius new = hashinit_flags((V_ng_ID_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask, 1016231831Sglebius HASH_NOWAIT); 1017231831Sglebius if (new == NULL) 1018231831Sglebius return; 1019231831Sglebius 1020231831Sglebius for (i = 0; i <= V_ng_ID_hmask; i++) 1021231831Sglebius LIST_FOREACH_SAFE(node, &V_ng_ID_hash[i], nd_idnodes, node2) { 1022231831Sglebius#ifdef INVARIANTS 1023231831Sglebius LIST_REMOVE(node, nd_idnodes); 1024231831Sglebius#endif 1025231831Sglebius hash = (node->nd_ID % (hmask + 1)); 1026231831Sglebius LIST_INSERT_HEAD(&new[hash], node, nd_idnodes); 1027231831Sglebius } 1028231831Sglebius 1029231831Sglebius hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_name_hmask); 1030231831Sglebius V_ng_ID_hash = new; 1031231831Sglebius V_ng_ID_hmask = hmask; 1032231831Sglebius} 1033231831Sglebius 103452419Sjulian/************************************************************************ 103552419Sjulian Hook routines 103652419Sjulian Names are not optional. Hooks are always connected, except for a 103770939Sjulian brief moment within these routines. On invalidation or during creation 103870939Sjulian they are connected to the 'dead' hook. 103952419Sjulian************************************************************************/ 104052419Sjulian 104152419Sjulian/* 104252419Sjulian * Remove a hook reference 104352419Sjulian */ 104470784Sjulianvoid 104552419Sjulianng_unref_hook(hook_p hook) 104652419Sjulian{ 104771047Sjulian 1048223754Sglebius if (hook == &ng_deadhook) 104971047Sjulian return; 105069519Sjulian 1051223754Sglebius if (refcount_release(&hook->hk_refs)) { /* we were the last */ 1052177722Smav if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */ 105371047Sjulian _NG_NODE_UNREF((_NG_HOOK_NODE(hook))); 105470700Sjulian NG_FREE_HOOK(hook); 105570700Sjulian } 105652419Sjulian} 105752419Sjulian 105852419Sjulian/* 105952419Sjulian * Add an unconnected hook to a node. Only used internally. 106070939Sjulian * Assumes node is locked. (XXX not yet true ) 106152419Sjulian */ 106252419Sjulianstatic int 106352419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp) 106452419Sjulian{ 106552419Sjulian hook_p hook; 106652419Sjulian int error = 0; 106752419Sjulian 106852419Sjulian /* Check that the given name is good */ 106952419Sjulian if (name == NULL) { 107071047Sjulian TRAP_ERROR(); 107152419Sjulian return (EINVAL); 107252419Sjulian } 107354096Sarchie if (ng_findhook(node, name) != NULL) { 107471047Sjulian TRAP_ERROR(); 107554096Sarchie return (EEXIST); 107652419Sjulian } 107752419Sjulian 107852419Sjulian /* Allocate the hook and link it up */ 107970784Sjulian NG_ALLOC_HOOK(hook); 108052419Sjulian if (hook == NULL) { 108171047Sjulian TRAP_ERROR(); 108252419Sjulian return (ENOMEM); 108352419Sjulian } 108470939Sjulian hook->hk_refs = 1; /* add a reference for us to return */ 108570784Sjulian hook->hk_flags = HK_INVALID; 108670939Sjulian hook->hk_peer = &ng_deadhook; /* start off this way */ 108770784Sjulian hook->hk_node = node; 108870784Sjulian NG_NODE_REF(node); /* each hook counts as a reference */ 108952419Sjulian 109070939Sjulian /* Set hook name */ 1091125028Sharti strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ); 109270939Sjulian 109370939Sjulian /* 109470939Sjulian * Check if the node type code has something to say about it 109570939Sjulian * If it fails, the unref of the hook will also unref the node. 109670939Sjulian */ 109770935Sjulian if (node->nd_type->newhook != NULL) { 109870935Sjulian if ((error = (*node->nd_type->newhook)(node, hook, name))) { 109970784Sjulian NG_HOOK_UNREF(hook); /* this frees the hook */ 110070700Sjulian return (error); 110170700Sjulian } 110270935Sjulian } 110352419Sjulian /* 110452419Sjulian * The 'type' agrees so far, so go ahead and link it in. 110552419Sjulian * We'll ask again later when we actually connect the hooks. 110652419Sjulian */ 110770784Sjulian LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 110870784Sjulian node->nd_numhooks++; 110970939Sjulian NG_HOOK_REF(hook); /* one for the node */ 111052419Sjulian 111152419Sjulian if (hookp) 111252419Sjulian *hookp = hook; 111370939Sjulian return (0); 111452419Sjulian} 111552419Sjulian 111652419Sjulian/* 111754096Sarchie * Find a hook 111854096Sarchie * 111954096Sarchie * Node types may supply their own optimized routines for finding 112054096Sarchie * hooks. If none is supplied, we just do a linear search. 112170939Sjulian * XXX Possibly we should add a reference to the hook? 112254096Sarchie */ 112354096Sarchiehook_p 112454096Sarchieng_findhook(node_p node, const char *name) 112554096Sarchie{ 112654096Sarchie hook_p hook; 112754096Sarchie 112870784Sjulian if (node->nd_type->findhook != NULL) 112970784Sjulian return (*node->nd_type->findhook)(node, name); 113070784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 1131229003Sglebius if (NG_HOOK_IS_VALID(hook) && 1132229003Sglebius (strcmp(NG_HOOK_NAME(hook), name) == 0)) 113354096Sarchie return (hook); 113454096Sarchie } 113554096Sarchie return (NULL); 113654096Sarchie} 113754096Sarchie 113854096Sarchie/* 113952419Sjulian * Destroy a hook 114052419Sjulian * 114152419Sjulian * As hooks are always attached, this really destroys two hooks. 114252419Sjulian * The one given, and the one attached to it. Disconnect the hooks 114370939Sjulian * from each other first. We reconnect the peer hook to the 'dead' 114470939Sjulian * hook so that it can still exist after we depart. We then 114570939Sjulian * send the peer its own destroy message. This ensures that we only 1146152451Sglebius * interact with the peer's structures when it is locked processing that 114770939Sjulian * message. We hold a reference to the peer hook so we are guaranteed that 114870939Sjulian * the peer hook and node are still going to exist until 114970939Sjulian * we are finished there as the hook holds a ref on the node. 1150152451Sglebius * We run this same code again on the peer hook, but that time it is already 1151152451Sglebius * attached to the 'dead' hook. 115271047Sjulian * 1153152451Sglebius * This routine is called at all stages of hook creation 115471047Sjulian * on error detection and must be able to handle any such stage. 115552419Sjulian */ 115652419Sjulianvoid 115752419Sjulianng_destroy_hook(hook_p hook) 115852419Sjulian{ 1159151974Sglebius hook_p peer; 1160151974Sglebius node_p node; 116152419Sjulian 116271047Sjulian if (hook == &ng_deadhook) { /* better safe than sorry */ 116371047Sjulian printf("ng_destroy_hook called on deadhook\n"); 116471047Sjulian return; 116571047Sjulian } 1166151974Sglebius 1167151974Sglebius /* 1168151974Sglebius * Protect divorce process with mutex, to avoid races on 1169151974Sglebius * simultaneous disconnect. 1170151974Sglebius */ 1171256550Smelifaro TOPOLOGY_WLOCK(); 1172151974Sglebius 1173151974Sglebius hook->hk_flags |= HK_INVALID; 1174151974Sglebius 1175151974Sglebius peer = NG_HOOK_PEER(hook); 1176151974Sglebius node = NG_HOOK_NODE(hook); 1177151974Sglebius 117870939Sjulian if (peer && (peer != &ng_deadhook)) { 117970939Sjulian /* 118070939Sjulian * Set the peer to point to ng_deadhook 118170939Sjulian * from this moment on we are effectively independent it. 118270939Sjulian * send it an rmhook message of it's own. 118370939Sjulian */ 118470939Sjulian peer->hk_peer = &ng_deadhook; /* They no longer know us */ 118570939Sjulian hook->hk_peer = &ng_deadhook; /* Nor us, them */ 118671047Sjulian if (NG_HOOK_NODE(peer) == &ng_deadnode) { 1187152451Sglebius /* 118871047Sjulian * If it's already divorced from a node, 118971047Sjulian * just free it. 119071047Sjulian */ 1191256550Smelifaro TOPOLOGY_WUNLOCK(); 119271047Sjulian } else { 1193256550Smelifaro TOPOLOGY_WUNLOCK(); 119471047Sjulian ng_rmhook_self(peer); /* Send it a surprise */ 119571047Sjulian } 119670942Sjulian NG_HOOK_UNREF(peer); /* account for peer link */ 119770942Sjulian NG_HOOK_UNREF(hook); /* account for peer link */ 1198151974Sglebius } else 1199256550Smelifaro TOPOLOGY_WUNLOCK(); 120052419Sjulian 1201256550Smelifaro TOPOLOGY_NOTOWNED(); 1202151974Sglebius 120352419Sjulian /* 120452419Sjulian * Remove the hook from the node's list to avoid possible recursion 120552419Sjulian * in case the disconnection results in node shutdown. 120652419Sjulian */ 120771047Sjulian if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */ 120871047Sjulian return; 120971047Sjulian } 121070784Sjulian LIST_REMOVE(hook, hk_hooks); 121170784Sjulian node->nd_numhooks--; 121270784Sjulian if (node->nd_type->disconnect) { 121352419Sjulian /* 121471047Sjulian * The type handler may elect to destroy the node so don't 1215167677Srwatson * trust its existence after this point. (except 121671047Sjulian * that we still hold a reference on it. (which we 121771047Sjulian * inherrited from the hook we are destroying) 121852419Sjulian */ 121970784Sjulian (*node->nd_type->disconnect) (hook); 122052419Sjulian } 122171047Sjulian 122271047Sjulian /* 122371047Sjulian * Note that because we will point to ng_deadnode, the original node 122471047Sjulian * is not decremented automatically so we do that manually. 122571047Sjulian */ 122671047Sjulian _NG_HOOK_NODE(hook) = &ng_deadnode; 122771047Sjulian NG_NODE_UNREF(node); /* We no longer point to it so adjust count */ 122871047Sjulian NG_HOOK_UNREF(hook); /* Account for linkage (in list) to node */ 122952419Sjulian} 123052419Sjulian 123152419Sjulian/* 123252419Sjulian * Take two hooks on a node and merge the connection so that the given node 123352419Sjulian * is effectively bypassed. 123452419Sjulian */ 123552419Sjulianint 123652419Sjulianng_bypass(hook_p hook1, hook_p hook2) 123752419Sjulian{ 123870784Sjulian if (hook1->hk_node != hook2->hk_node) { 123971047Sjulian TRAP_ERROR(); 124052419Sjulian return (EINVAL); 124170784Sjulian } 1242256550Smelifaro TOPOLOGY_WLOCK(); 1243231761Sglebius if (NG_HOOK_NOT_VALID(hook1) || NG_HOOK_NOT_VALID(hook2)) { 1244256550Smelifaro TOPOLOGY_WUNLOCK(); 1245231761Sglebius return (EINVAL); 1246231761Sglebius } 124770784Sjulian hook1->hk_peer->hk_peer = hook2->hk_peer; 124870784Sjulian hook2->hk_peer->hk_peer = hook1->hk_peer; 124952419Sjulian 125070939Sjulian hook1->hk_peer = &ng_deadhook; 125170939Sjulian hook2->hk_peer = &ng_deadhook; 1252256550Smelifaro TOPOLOGY_WUNLOCK(); 125370939Sjulian 1254163244Sglebius NG_HOOK_UNREF(hook1); 1255163244Sglebius NG_HOOK_UNREF(hook2); 1256163244Sglebius 125752419Sjulian /* XXX If we ever cache methods on hooks update them as well */ 125852419Sjulian ng_destroy_hook(hook1); 125952419Sjulian ng_destroy_hook(hook2); 126052419Sjulian return (0); 126152419Sjulian} 126252419Sjulian 126352419Sjulian/* 126452419Sjulian * Install a new netgraph type 126552419Sjulian */ 126652419Sjulianint 126752419Sjulianng_newtype(struct ng_type *tp) 126852419Sjulian{ 126952419Sjulian const size_t namelen = strlen(tp->name); 127052419Sjulian 127152419Sjulian /* Check version and type name fields */ 1272229003Sglebius if ((tp->version != NG_ABI_VERSION) || (namelen == 0) || 1273229003Sglebius (namelen >= NG_TYPESIZ)) { 127471047Sjulian TRAP_ERROR(); 1275131374Sjulian if (tp->version != NG_ABI_VERSION) { 1276229003Sglebius printf("Netgraph: Node type rejected. ABI mismatch. " 1277229003Sglebius "Suggest recompile\n"); 1278131374Sjulian } 127952419Sjulian return (EINVAL); 128052419Sjulian } 128152419Sjulian 128252419Sjulian /* Check for name collision */ 128352419Sjulian if (ng_findtype(tp->name) != NULL) { 128471047Sjulian TRAP_ERROR(); 128552419Sjulian return (EEXIST); 128652419Sjulian } 128752419Sjulian 128852419Sjulian /* Link in new type */ 1289230480Sglebius TYPELIST_WLOCK(); 129070700Sjulian LIST_INSERT_HEAD(&ng_typelist, tp, types); 129171603Sjulian tp->refs = 1; /* first ref is linked list */ 1292230480Sglebius TYPELIST_WUNLOCK(); 129352419Sjulian return (0); 129452419Sjulian} 129552419Sjulian 129652419Sjulian/* 129780222Sjulian * unlink a netgraph type 129880222Sjulian * If no examples exist 129980222Sjulian */ 130080222Sjulianint 130180222Sjulianng_rmtype(struct ng_type *tp) 130280222Sjulian{ 130380222Sjulian /* Check for name collision */ 130480222Sjulian if (tp->refs != 1) { 130580222Sjulian TRAP_ERROR(); 130680222Sjulian return (EBUSY); 130780222Sjulian } 130880222Sjulian 130980222Sjulian /* Unlink type */ 1310230480Sglebius TYPELIST_WLOCK(); 131180222Sjulian LIST_REMOVE(tp, types); 1312230480Sglebius TYPELIST_WUNLOCK(); 131380222Sjulian return (0); 131480222Sjulian} 131580222Sjulian 131680222Sjulian/* 131752419Sjulian * Look for a type of the name given 131852419Sjulian */ 131952419Sjulianstruct ng_type * 132052419Sjulianng_findtype(const char *typename) 132152419Sjulian{ 132252419Sjulian struct ng_type *type; 132352419Sjulian 1324230480Sglebius TYPELIST_RLOCK(); 132570700Sjulian LIST_FOREACH(type, &ng_typelist, types) { 132652419Sjulian if (strcmp(type->name, typename) == 0) 132752419Sjulian break; 132852419Sjulian } 1329230480Sglebius TYPELIST_RUNLOCK(); 133052419Sjulian return (type); 133152419Sjulian} 133252419Sjulian 133352419Sjulian/************************************************************************ 133452419Sjulian Composite routines 133552419Sjulian************************************************************************/ 133652419Sjulian/* 133771047Sjulian * Connect two nodes using the specified hooks, using queued functions. 133852419Sjulian */ 1339172806Smavstatic int 1340172806Smavng_con_part3(node_p node, item_p item, hook_p hook) 134152419Sjulian{ 1342172806Smav int error = 0; 134352419Sjulian 134471047Sjulian /* 134571047Sjulian * When we run, we know that the node 'node' is locked for us. 134671047Sjulian * Our caller has a reference on the hook. 134771047Sjulian * Our caller has a reference on the node. 134871047Sjulian * (In this case our caller is ng_apply_item() ). 134971047Sjulian * The peer hook has a reference on the hook. 135071849Sjulian * We are all set up except for the final call to the node, and 135171849Sjulian * the clearing of the INVALID flag. 135271047Sjulian */ 135371047Sjulian if (NG_HOOK_NODE(hook) == &ng_deadnode) { 135471047Sjulian /* 135571047Sjulian * The node must have been freed again since we last visited 135671047Sjulian * here. ng_destry_hook() has this effect but nothing else does. 135771047Sjulian * We should just release our references and 135871047Sjulian * free anything we can think of. 135971047Sjulian * Since we know it's been destroyed, and it's our caller 136071047Sjulian * that holds the references, just return. 136171047Sjulian */ 1362172806Smav ERROUT(ENOENT); 136352419Sjulian } 136471047Sjulian if (hook->hk_node->nd_type->connect) { 1365172806Smav if ((error = (*hook->hk_node->nd_type->connect) (hook))) { 136671047Sjulian ng_destroy_hook(hook); /* also zaps peer */ 136771849Sjulian printf("failed in ng_con_part3()\n"); 1368172806Smav ERROUT(error); 136971047Sjulian } 137052419Sjulian } 137171047Sjulian /* 137271047Sjulian * XXX this is wrong for SMP. Possibly we need 137371047Sjulian * to separate out 'create' and 'invalid' flags. 137471047Sjulian * should only set flags on hooks we have locked under our node. 137571047Sjulian */ 137671047Sjulian hook->hk_flags &= ~HK_INVALID; 1377172806Smavdone: 1378172806Smav NG_FREE_ITEM(item); 1379172806Smav return (error); 138071047Sjulian} 138152419Sjulian 1382172806Smavstatic int 1383172806Smavng_con_part2(node_p node, item_p item, hook_p hook) 138471047Sjulian{ 1385172806Smav hook_p peer; 1386172806Smav int error = 0; 138771047Sjulian 138852419Sjulian /* 138971047Sjulian * When we run, we know that the node 'node' is locked for us. 139071047Sjulian * Our caller has a reference on the hook. 139171047Sjulian * Our caller has a reference on the node. 139271047Sjulian * (In this case our caller is ng_apply_item() ). 139371047Sjulian * The peer hook has a reference on the hook. 139471047Sjulian * our node pointer points to the 'dead' node. 139571047Sjulian * First check the hook name is unique. 139671849Sjulian * Should not happen because we checked before queueing this. 139752419Sjulian */ 139871047Sjulian if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) { 139971047Sjulian TRAP_ERROR(); 140071047Sjulian ng_destroy_hook(hook); /* should destroy peer too */ 140171849Sjulian printf("failed in ng_con_part2()\n"); 1402172806Smav ERROUT(EEXIST); 140371047Sjulian } 140470939Sjulian /* 140571047Sjulian * Check if the node type code has something to say about it 140671047Sjulian * If it fails, the unref of the hook will also unref the attached node, 140771047Sjulian * however since that node is 'ng_deadnode' this will do nothing. 140871047Sjulian * The peer hook will also be destroyed. 140970939Sjulian */ 141071047Sjulian if (node->nd_type->newhook != NULL) { 1411172806Smav if ((error = (*node->nd_type->newhook)(node, hook, 1412172806Smav hook->hk_name))) { 141371047Sjulian ng_destroy_hook(hook); /* should destroy peer too */ 141471849Sjulian printf("failed in ng_con_part2()\n"); 1415172806Smav ERROUT(error); 141671047Sjulian } 141771047Sjulian } 141871047Sjulian 141971047Sjulian /* 142071047Sjulian * The 'type' agrees so far, so go ahead and link it in. 142171047Sjulian * We'll ask again later when we actually connect the hooks. 142271047Sjulian */ 142371047Sjulian hook->hk_node = node; /* just overwrite ng_deadnode */ 142471047Sjulian NG_NODE_REF(node); /* each hook counts as a reference */ 142571047Sjulian LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 142671047Sjulian node->nd_numhooks++; 142771047Sjulian NG_HOOK_REF(hook); /* one for the node */ 142871047Sjulian 142971047Sjulian /* 1430167677Srwatson * We now have a symmetrical situation, where both hooks have been 143174078Sjulian * linked to their nodes, the newhook methods have been called 143271047Sjulian * And the references are all correct. The hooks are still marked 143371047Sjulian * as invalid, as we have not called the 'connect' methods 143471047Sjulian * yet. 1435167677Srwatson * We can call the local one immediately as we have the 143671047Sjulian * node locked, but we need to queue the remote one. 143771047Sjulian */ 143871047Sjulian if (hook->hk_node->nd_type->connect) { 1439172806Smav if ((error = (*hook->hk_node->nd_type->connect) (hook))) { 144071047Sjulian ng_destroy_hook(hook); /* also zaps peer */ 144171849Sjulian printf("failed in ng_con_part2(A)\n"); 1442172806Smav ERROUT(error); 144371047Sjulian } 144471047Sjulian } 1445151974Sglebius 1446151974Sglebius /* 1447151974Sglebius * Acquire topo mutex to avoid race with ng_destroy_hook(). 1448151974Sglebius */ 1449256550Smelifaro TOPOLOGY_RLOCK(); 1450151974Sglebius peer = hook->hk_peer; 1451151974Sglebius if (peer == &ng_deadhook) { 1452256550Smelifaro TOPOLOGY_RUNLOCK(); 1453151974Sglebius printf("failed in ng_con_part2(B)\n"); 1454151974Sglebius ng_destroy_hook(hook); 1455172806Smav ERROUT(ENOENT); 1456151974Sglebius } 1457256550Smelifaro TOPOLOGY_RUNLOCK(); 1458151974Sglebius 1459173605Sglebius if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3, 1460173605Sglebius NULL, 0, NG_REUSE_ITEM))) { 1461151974Sglebius printf("failed in ng_con_part2(C)\n"); 146271849Sjulian ng_destroy_hook(hook); /* also zaps peer */ 1463172806Smav return (error); /* item was consumed. */ 146471849Sjulian } 146571047Sjulian hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */ 1466172806Smav return (0); /* item was consumed. */ 1467172806Smavdone: 1468172806Smav NG_FREE_ITEM(item); 1469172806Smav return (error); 147052419Sjulian} 147152419Sjulian 147252419Sjulian/* 1473152451Sglebius * Connect this node with another node. We assume that this node is 147471047Sjulian * currently locked, as we are only called from an NGM_CONNECT message. 147552419Sjulian */ 147671047Sjulianstatic int 1477172806Smavng_con_nodes(item_p item, node_p node, const char *name, 1478172806Smav node_p node2, const char *name2) 147952419Sjulian{ 1480152451Sglebius int error; 1481152451Sglebius hook_p hook; 1482152451Sglebius hook_p hook2; 148352419Sjulian 148471849Sjulian if (ng_findhook(node2, name2) != NULL) { 148571849Sjulian return(EEXIST); 148671849Sjulian } 148770939Sjulian if ((error = ng_add_hook(node, name, &hook))) /* gives us a ref */ 148852419Sjulian return (error); 148971047Sjulian /* Allocate the other hook and link it up */ 149071047Sjulian NG_ALLOC_HOOK(hook2); 1491140737Sglebius if (hook2 == NULL) { 149271047Sjulian TRAP_ERROR(); 149371047Sjulian ng_destroy_hook(hook); /* XXX check ref counts so far */ 149471047Sjulian NG_HOOK_UNREF(hook); /* including our ref */ 149571047Sjulian return (ENOMEM); 149671047Sjulian } 149771047Sjulian hook2->hk_refs = 1; /* start with a reference for us. */ 149871047Sjulian hook2->hk_flags = HK_INVALID; 149971047Sjulian hook2->hk_peer = hook; /* Link the two together */ 150071047Sjulian hook->hk_peer = hook2; 150171047Sjulian NG_HOOK_REF(hook); /* Add a ref for the peer to each*/ 150271047Sjulian NG_HOOK_REF(hook2); 1503152451Sglebius hook2->hk_node = &ng_deadnode; 1504125028Sharti strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ); 150571047Sjulian 150671047Sjulian /* 150771047Sjulian * Queue the function above. 150871047Sjulian * Procesing continues in that function in the lock context of 150971047Sjulian * the other node. 151071047Sjulian */ 1511173605Sglebius if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0, 1512173605Sglebius NG_NOFLAGS))) { 1513171885Smav printf("failed in ng_con_nodes(): %d\n", error); 1514171885Smav ng_destroy_hook(hook); /* also zaps peer */ 1515171885Smav } 151671047Sjulian 151771047Sjulian NG_HOOK_UNREF(hook); /* Let each hook go if it wants to */ 151871047Sjulian NG_HOOK_UNREF(hook2); 1519171885Smav return (error); 152071047Sjulian} 152171047Sjulian 152271047Sjulian/* 152371047Sjulian * Make a peer and connect. 152471047Sjulian * We assume that the local node is locked. 152571047Sjulian * The new node probably doesn't need a lock until 152671047Sjulian * it has a hook, because it cannot really have any work until then, 152771047Sjulian * but we should think about it a bit more. 152871047Sjulian * 152971047Sjulian * The problem may come if the other node also fires up 153071047Sjulian * some hardware or a timer or some other source of activation, 153171047Sjulian * also it may already get a command msg via it's ID. 153271047Sjulian * 153371047Sjulian * We could use the same method as ng_con_nodes() but we'd have 1534152451Sglebius * to add ability to remove the node when failing. (Not hard, just 153571047Sjulian * make arg1 point to the node to remove). 153671047Sjulian * Unless of course we just ignore failure to connect and leave 153771047Sjulian * an unconnected node? 153871047Sjulian */ 153971047Sjulianstatic int 154071047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type) 154171047Sjulian{ 1542152451Sglebius node_p node2; 1543152451Sglebius hook_p hook1, hook2; 1544152451Sglebius int error; 154571047Sjulian 154671047Sjulian if ((error = ng_make_node(type, &node2))) { 154752419Sjulian return (error); 154852419Sjulian } 154970939Sjulian 155071047Sjulian if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */ 155171849Sjulian ng_rmnode(node2, NULL, NULL, 0); 155271047Sjulian return (error); 155371047Sjulian } 155471047Sjulian 155571047Sjulian if ((error = ng_add_hook(node2, name2, &hook2))) { 155671849Sjulian ng_rmnode(node2, NULL, NULL, 0); 155771047Sjulian ng_destroy_hook(hook1); 155871047Sjulian NG_HOOK_UNREF(hook1); 155971047Sjulian return (error); 156071047Sjulian } 156171047Sjulian 156270939Sjulian /* 156371047Sjulian * Actually link the two hooks together. 156471047Sjulian */ 156571047Sjulian hook1->hk_peer = hook2; 156671047Sjulian hook2->hk_peer = hook1; 156771047Sjulian 156871047Sjulian /* Each hook is referenced by the other */ 156971047Sjulian NG_HOOK_REF(hook1); 157071047Sjulian NG_HOOK_REF(hook2); 157171047Sjulian 157271047Sjulian /* Give each node the opportunity to veto the pending connection */ 157371047Sjulian if (hook1->hk_node->nd_type->connect) { 157471047Sjulian error = (*hook1->hk_node->nd_type->connect) (hook1); 157571047Sjulian } 157671047Sjulian 157771047Sjulian if ((error == 0) && hook2->hk_node->nd_type->connect) { 157871047Sjulian error = (*hook2->hk_node->nd_type->connect) (hook2); 157971047Sjulian 158071047Sjulian } 158171047Sjulian 158271047Sjulian /* 158370939Sjulian * drop the references we were holding on the two hooks. 158470939Sjulian */ 158571047Sjulian if (error) { 158671047Sjulian ng_destroy_hook(hook2); /* also zaps hook1 */ 158771849Sjulian ng_rmnode(node2, NULL, NULL, 0); 158871047Sjulian } else { 158971047Sjulian /* As a last act, allow the hooks to be used */ 159071047Sjulian hook1->hk_flags &= ~HK_INVALID; 159171047Sjulian hook2->hk_flags &= ~HK_INVALID; 159271047Sjulian } 159371047Sjulian NG_HOOK_UNREF(hook1); 159470939Sjulian NG_HOOK_UNREF(hook2); 159570939Sjulian return (error); 159652419Sjulian} 159771047Sjulian 159870700Sjulian/************************************************************************ 159970700Sjulian Utility routines to send self messages 160070700Sjulian************************************************************************/ 160170700Sjulian 160271849Sjulian/* Shut this node down as soon as everyone is clear of it */ 1603167677Srwatson/* Should add arg "immediately" to jump the queue */ 160470700Sjulianint 1605186060Smavng_rmnode_self(node_p node) 160670700Sjulian{ 160771849Sjulian int error; 160852419Sjulian 160971849Sjulian if (node == &ng_deadnode) 161071849Sjulian return (0); 1611132464Sjulian node->nd_flags |= NGF_INVALID; 1612132464Sjulian if (node->nd_flags & NGF_CLOSING) 161371849Sjulian return (0); 161470700Sjulian 1615186060Smav error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0); 161671849Sjulian return (error); 161770700Sjulian} 161870700Sjulian 161971849Sjulianstatic void 162071047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2) 162171047Sjulian{ 162271047Sjulian ng_destroy_hook(hook); 162371849Sjulian return ; 162471047Sjulian} 162571047Sjulian 162670935Sjulianint 162770935Sjulianng_rmhook_self(hook_p hook) 162870935Sjulian{ 162971047Sjulian int error; 163070935Sjulian node_p node = NG_HOOK_NODE(hook); 163170935Sjulian 163271047Sjulian if (node == &ng_deadnode) 163371047Sjulian return (0); 163471047Sjulian 163571047Sjulian error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0); 163671047Sjulian return (error); 163770935Sjulian} 163870935Sjulian 163970700Sjulian/*********************************************************************** 164052419Sjulian * Parse and verify a string of the form: <NODE:><PATH> 164152419Sjulian * 164252419Sjulian * Such a string can refer to a specific node or a specific hook 164352419Sjulian * on a specific node, depending on how you look at it. In the 164452419Sjulian * latter case, the PATH component must not end in a dot. 164552419Sjulian * 164652419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string 164752419Sjulian * of hook names separated by dots. This breaks out the original 164852419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp 164952419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to 165052419Sjulian * the final hook component of <PATH>, if any, otherwise NULL. 165152419Sjulian * 165252419Sjulian * This returns -1 if the path is malformed. The char ** are optional. 165370700Sjulian ***********************************************************************/ 165452419Sjulianint 165552419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 165652419Sjulian{ 1657152451Sglebius char *node, *path, *hook; 1658152451Sglebius int k; 165952419Sjulian 166052419Sjulian /* 166152419Sjulian * Extract absolute NODE, if any 166252419Sjulian */ 166352419Sjulian for (path = addr; *path && *path != ':'; path++); 166452419Sjulian if (*path) { 166552419Sjulian node = addr; /* Here's the NODE */ 166652419Sjulian *path++ = '\0'; /* Here's the PATH */ 166752419Sjulian 166852419Sjulian /* Node name must not be empty */ 166952419Sjulian if (!*node) 167052419Sjulian return -1; 167152419Sjulian 167252419Sjulian /* A name of "." is OK; otherwise '.' not allowed */ 167352419Sjulian if (strcmp(node, ".") != 0) { 167452419Sjulian for (k = 0; node[k]; k++) 167552419Sjulian if (node[k] == '.') 167652419Sjulian return -1; 167752419Sjulian } 167852419Sjulian } else { 167952419Sjulian node = NULL; /* No absolute NODE */ 168052419Sjulian path = addr; /* Here's the PATH */ 168152419Sjulian } 168252419Sjulian 168352419Sjulian /* Snoop for illegal characters in PATH */ 168452419Sjulian for (k = 0; path[k]; k++) 168552419Sjulian if (path[k] == ':') 168652419Sjulian return -1; 168752419Sjulian 168852419Sjulian /* Check for no repeated dots in PATH */ 168952419Sjulian for (k = 0; path[k]; k++) 169052419Sjulian if (path[k] == '.' && path[k + 1] == '.') 169152419Sjulian return -1; 169252419Sjulian 169352419Sjulian /* Remove extra (degenerate) dots from beginning or end of PATH */ 169452419Sjulian if (path[0] == '.') 169552419Sjulian path++; 169652419Sjulian if (*path && path[strlen(path) - 1] == '.') 169752419Sjulian path[strlen(path) - 1] = 0; 169852419Sjulian 169952419Sjulian /* If PATH has a dot, then we're not talking about a hook */ 170052419Sjulian if (*path) { 170152419Sjulian for (hook = path, k = 0; path[k]; k++) 170252419Sjulian if (path[k] == '.') { 170352419Sjulian hook = NULL; 170452419Sjulian break; 170552419Sjulian } 170652419Sjulian } else 170752419Sjulian path = hook = NULL; 170852419Sjulian 170952419Sjulian /* Done */ 171052419Sjulian if (nodep) 171152419Sjulian *nodep = node; 171252419Sjulian if (pathp) 171352419Sjulian *pathp = path; 171452419Sjulian if (hookp) 171552419Sjulian *hookp = hook; 171652419Sjulian return (0); 171752419Sjulian} 171852419Sjulian 171952419Sjulian/* 172052419Sjulian * Given a path, which may be absolute or relative, and a starting node, 172170700Sjulian * return the destination node. 172252419Sjulian */ 172352419Sjulianint 1724229003Sglebiusng_path2noderef(node_p here, const char *address, node_p *destp, 1725229003Sglebius hook_p *lasthook) 172652419Sjulian{ 1727125028Sharti char fullpath[NG_PATHSIZ]; 1728219827Sglebius char *nodename, *path; 172970700Sjulian node_p node, oldnode; 173052419Sjulian 173152419Sjulian /* Initialize */ 173270784Sjulian if (destp == NULL) { 173371047Sjulian TRAP_ERROR(); 173452419Sjulian return EINVAL; 173570784Sjulian } 173652419Sjulian *destp = NULL; 173752419Sjulian 173852419Sjulian /* Make a writable copy of address for ng_path_parse() */ 173952419Sjulian strncpy(fullpath, address, sizeof(fullpath) - 1); 174052419Sjulian fullpath[sizeof(fullpath) - 1] = '\0'; 174152419Sjulian 174252419Sjulian /* Parse out node and sequence of hooks */ 174352419Sjulian if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 174471047Sjulian TRAP_ERROR(); 174552419Sjulian return EINVAL; 174652419Sjulian } 174752419Sjulian 174870700Sjulian /* 174970700Sjulian * For an absolute address, jump to the starting node. 175070700Sjulian * Note that this holds a reference on the node for us. 175170700Sjulian * Don't forget to drop the reference if we don't need it. 175270700Sjulian */ 175352419Sjulian if (nodename) { 175470700Sjulian node = ng_name2noderef(here, nodename); 175552419Sjulian if (node == NULL) { 175671047Sjulian TRAP_ERROR(); 175752419Sjulian return (ENOENT); 175852419Sjulian } 175970700Sjulian } else { 176070700Sjulian if (here == NULL) { 176171047Sjulian TRAP_ERROR(); 176270700Sjulian return (EINVAL); 176370700Sjulian } 176452419Sjulian node = here; 176570784Sjulian NG_NODE_REF(node); 176670700Sjulian } 176752419Sjulian 1768219827Sglebius if (path == NULL) { 1769219827Sglebius if (lasthook != NULL) 1770219827Sglebius *lasthook = NULL; 1771219827Sglebius *destp = node; 1772219827Sglebius return (0); 1773219827Sglebius } 1774219827Sglebius 177570700Sjulian /* 1776152451Sglebius * Now follow the sequence of hooks 1777219827Sglebius * 1778219827Sglebius * XXXGL: The path may demolish as we go the sequence, but if 1779219827Sglebius * we hold the topology mutex at critical places, then, I hope, 1780219827Sglebius * we would always have valid pointers in hand, although the 1781219827Sglebius * path behind us may no longer exist. 178270700Sjulian */ 1783219827Sglebius for (;;) { 1784219827Sglebius hook_p hook; 178552419Sjulian char *segment; 178652419Sjulian 178752419Sjulian /* 178852419Sjulian * Break out the next path segment. Replace the dot we just 1789219827Sglebius * found with a NUL; "path" points to the next segment (or the 179052419Sjulian * NUL at the end). 179152419Sjulian */ 1792219827Sglebius for (segment = path; *path != '\0'; path++) { 1793219827Sglebius if (*path == '.') { 1794219827Sglebius *path++ = '\0'; 179552419Sjulian break; 179652419Sjulian } 179752419Sjulian } 179852419Sjulian 179952419Sjulian /* We have a segment, so look for a hook by that name */ 180054096Sarchie hook = ng_findhook(node, segment); 180152419Sjulian 1802256550Smelifaro TOPOLOGY_WLOCK(); 180352419Sjulian /* Can't get there from here... */ 1804229003Sglebius if (hook == NULL || NG_HOOK_PEER(hook) == NULL || 1805229003Sglebius NG_HOOK_NOT_VALID(hook) || 1806229003Sglebius NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) { 180771047Sjulian TRAP_ERROR(); 180870784Sjulian NG_NODE_UNREF(node); 1809256550Smelifaro TOPOLOGY_WUNLOCK(); 181052419Sjulian return (ENOENT); 181152419Sjulian } 181252419Sjulian 181370700Sjulian /* 1814152451Sglebius * Hop on over to the next node 181570700Sjulian * XXX 1816152451Sglebius * Big race conditions here as hooks and nodes go away 181770700Sjulian * *** Idea.. store an ng_ID_t in each hook and use that 181870700Sjulian * instead of the direct hook in this crawl? 181970700Sjulian */ 182070700Sjulian oldnode = node; 182170784Sjulian if ((node = NG_PEER_NODE(hook))) 182270784Sjulian NG_NODE_REF(node); /* XXX RACE */ 182370784Sjulian NG_NODE_UNREF(oldnode); /* XXX another race */ 182470784Sjulian if (NG_NODE_NOT_VALID(node)) { 182570784Sjulian NG_NODE_UNREF(node); /* XXX more races */ 1826256550Smelifaro TOPOLOGY_WUNLOCK(); 1827219827Sglebius TRAP_ERROR(); 1828219827Sglebius return (ENXIO); 182970700Sjulian } 183052419Sjulian 1831219827Sglebius if (*path == '\0') { 1832219827Sglebius if (lasthook != NULL) { 1833219827Sglebius if (hook != NULL) { 1834219827Sglebius *lasthook = NG_HOOK_PEER(hook); 1835219827Sglebius NG_HOOK_REF(*lasthook); 1836219827Sglebius } else 1837219827Sglebius *lasthook = NULL; 1838219827Sglebius } 1839256550Smelifaro TOPOLOGY_WUNLOCK(); 1840219827Sglebius *destp = node; 1841219827Sglebius return (0); 1842219827Sglebius } 1843256550Smelifaro TOPOLOGY_WUNLOCK(); 184452419Sjulian } 184552419Sjulian} 184652419Sjulian 184770700Sjulian/***************************************************************\ 184870700Sjulian* Input queue handling. 184970700Sjulian* All activities are submitted to the node via the input queue 185070700Sjulian* which implements a multiple-reader/single-writer gate. 1851167677Srwatson* Items which cannot be handled immediately are queued. 185270700Sjulian* 185370700Sjulian* read-write queue locking inline functions * 185470700Sjulian\***************************************************************/ 185570700Sjulian 1856178228Smavstatic __inline void ng_queue_rw(node_p node, item_p item, int rw); 1857178228Smavstatic __inline item_p ng_dequeue(node_p node, int *rw); 1858178228Smavstatic __inline item_p ng_acquire_read(node_p node, item_p item); 1859178228Smavstatic __inline item_p ng_acquire_write(node_p node, item_p item); 1860178228Smavstatic __inline void ng_leave_read(node_p node); 1861178228Smavstatic __inline void ng_leave_write(node_p node); 186270700Sjulian 186352419Sjulian/* 186470700Sjulian * Definition of the bits fields in the ng_queue flag word. 186570700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle 186670700Sjulian * with them. 186770700Sjulian * 186871902Sjulian * The ordering here may be important! don't shuffle these. 186952419Sjulian */ 187070700Sjulian/*- 187170700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet) 187270700Sjulian | 187370700Sjulian V 187470700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+ 1875151973Sglebius | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1876151973Sglebius | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A| 1877151973Sglebius | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W| 187870700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+ 1879151973Sglebius \___________________________ ____________________________/ | | 1880151973Sglebius V | | 1881151973Sglebius [active reader count] | | 188270700Sjulian | | 1883151973Sglebius Operation Pending -------------------------------+ | 188470700Sjulian | 1885151973Sglebius Active Writer ---------------------------------------+ 188671902Sjulian 1887178039SmavNode queue has such semantics: 1888178039Smav- All flags modifications are atomic. 1889178039Smav- Reader count can be incremented only if there is no writer or pending flags. 1890178039Smav As soon as this can't be done with single operation, it is implemented with 1891178039Smav spin loop and atomic_cmpset(). 1892178039Smav- Writer flag can be set only if there is no any bits set. 1893178039Smav It is implemented with atomic_cmpset(). 1894178039Smav- Pending flag can be set any time, but to avoid collision on queue processing 1895178039Smav all queue fields are protected by the mutex. 1896178039Smav- Queue processing thread reads queue holding the mutex, but releases it while 1897178039Smav processing. When queue is empty pending flag is removed. 1898178039Smav*/ 189971902Sjulian 1900151973Sglebius#define WRITER_ACTIVE 0x00000001 1901151973Sglebius#define OP_PENDING 0x00000002 1902151973Sglebius#define READER_INCREMENT 0x00000004 1903151973Sglebius#define READER_MASK 0xfffffffc /* Not valid if WRITER_ACTIVE is set */ 1904151973Sglebius#define SAFETY_BARRIER 0x00100000 /* 128K items queued should be enough */ 190571902Sjulian 190671902Sjulian/* Defines of more elaborate states on the queue */ 1907151973Sglebius/* Mask of bits a new read cares about */ 1908151973Sglebius#define NGQ_RMASK (WRITER_ACTIVE|OP_PENDING) 190971902Sjulian 1910151973Sglebius/* Mask of bits a new write cares about */ 191171902Sjulian#define NGQ_WMASK (NGQ_RMASK|READER_MASK) 191271902Sjulian 1913151973Sglebius/* Test to decide if there is something on the queue. */ 1914151973Sglebius#define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING) 191571902Sjulian 1916151973Sglebius/* How to decide what the next queued item is. */ 1917178228Smav#define HEAD_IS_READER(QP) NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue)) 1918178228Smav#define HEAD_IS_WRITER(QP) NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */ 1919151973Sglebius 1920151973Sglebius/* Read the status to decide if the next item on the queue can now run. */ 1921151973Sglebius#define QUEUED_READER_CAN_PROCEED(QP) \ 1922151973Sglebius (((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0) 1923151973Sglebius#define QUEUED_WRITER_CAN_PROCEED(QP) \ 1924151973Sglebius (((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0) 1925151973Sglebius 192671902Sjulian/* Is there a chance of getting ANY work off the queue? */ 1927151973Sglebius#define NEXT_QUEUED_ITEM_CAN_PROCEED(QP) \ 1928151973Sglebius ((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) : \ 1929177953Smav QUEUED_WRITER_CAN_PROCEED(QP)) 193071902Sjulian 1931151238Sglebius#define NGQRW_R 0 1932151238Sglebius#define NGQRW_W 1 1933151238Sglebius 1934178228Smav#define NGQ2_WORKQ 0x00000001 1935178228Smav 193670700Sjulian/* 193770700Sjulian * Taking into account the current state of the queue and node, possibly take 193870700Sjulian * the next entry off the queue and return it. Return NULL if there was 193970700Sjulian * nothing we could return, either because there really was nothing there, or 194070700Sjulian * because the node was in a state where it cannot yet process the next item 194170700Sjulian * on the queue. 194270700Sjulian */ 194370700Sjulianstatic __inline item_p 1944178228Smavng_dequeue(node_p node, int *rw) 194570700Sjulian{ 194670700Sjulian item_p item; 1947178228Smav struct ng_queue *ngq = &node->nd_input_queue; 194871902Sjulian 1949178039Smav /* This MUST be called with the mutex held. */ 1950139039Sglebius mtx_assert(&ngq->q_mtx, MA_OWNED); 1951178039Smav 1952178039Smav /* If there is nothing queued, then just return. */ 1953151973Sglebius if (!QUEUE_ACTIVE(ngq)) { 1954154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; " 1955154275Sglebius "queue flags 0x%lx", __func__, 1956178228Smav node->nd_ID, node, ngq->q_flags); 1957151973Sglebius return (NULL); 1958151973Sglebius } 1959139039Sglebius 1960151973Sglebius /* 1961151973Sglebius * From here, we can assume there is a head item. 1962151973Sglebius * We need to find out what it is and if it can be dequeued, given 1963151973Sglebius * the current state of the node. 1964151973Sglebius */ 1965151973Sglebius if (HEAD_IS_READER(ngq)) { 1966177953Smav while (1) { 1967177953Smav long t = ngq->q_flags; 1968177953Smav if (t & WRITER_ACTIVE) { 1969178039Smav /* There is writer, reader can't proceed. */ 1970229003Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) queued " 1971229003Sglebius "reader can't proceed; queue flags 0x%lx", 1972229003Sglebius __func__, node->nd_ID, node, t); 1973177953Smav return (NULL); 1974177953Smav } 1975178228Smav if (atomic_cmpset_acq_int(&ngq->q_flags, t, 1976177953Smav t + READER_INCREMENT)) 1977177953Smav break; 1978177953Smav cpu_spinwait(); 1979151973Sglebius } 1980178039Smav /* We have got reader lock for the node. */ 1981151238Sglebius *rw = NGQRW_R; 1982178228Smav } else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING, 1983177953Smav OP_PENDING + WRITER_ACTIVE)) { 1984178039Smav /* We have got writer lock for the node. */ 1985151238Sglebius *rw = NGQRW_W; 198670700Sjulian } else { 1987178039Smav /* There is somebody other, writer can't proceed. */ 1988229003Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer can't " 1989229003Sglebius "proceed; queue flags 0x%lx", __func__, node->nd_ID, node, 1990229003Sglebius ngq->q_flags); 1991151973Sglebius return (NULL); 199270700Sjulian } 199352419Sjulian 199470700Sjulian /* 199570700Sjulian * Now we dequeue the request (whatever it may be) and correct the 199670700Sjulian * pending flags and the next and last pointers. 199770700Sjulian */ 1998178228Smav item = STAILQ_FIRST(&ngq->queue); 1999178228Smav STAILQ_REMOVE_HEAD(&ngq->queue, el_next); 2000178228Smav if (STAILQ_EMPTY(&ngq->queue)) 2001178228Smav atomic_clear_int(&ngq->q_flags, OP_PENDING); 2002229003Sglebius CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; queue " 2003229003Sglebius "flags 0x%lx", __func__, node->nd_ID, node, item, *rw ? "WRITER" : 2004229003Sglebius "READER", ngq->q_flags); 200570700Sjulian return (item); 200670700Sjulian} 200752419Sjulian 200870700Sjulian/* 2009178039Smav * Queue a packet to be picked up later by someone else. 2010178039Smav * If the queue could be run now, add node to the queue handler's worklist. 201170700Sjulian */ 201270700Sjulianstatic __inline void 2013178228Smavng_queue_rw(node_p node, item_p item, int rw) 201470700Sjulian{ 2015178228Smav struct ng_queue *ngq = &node->nd_input_queue; 2016151973Sglebius if (rw == NGQRW_W) 2017151973Sglebius NGI_SET_WRITER(item); 2018151973Sglebius else 2019151973Sglebius NGI_SET_READER(item); 2020241009Srstone item->depth = 1; 2021177953Smav 2022177953Smav NG_QUEUE_LOCK(ngq); 2023177953Smav /* Set OP_PENDING flag and enqueue the item. */ 2024178228Smav atomic_set_int(&ngq->q_flags, OP_PENDING); 2025178228Smav STAILQ_INSERT_TAIL(&ngq->queue, item, el_next); 2026177953Smav 2027154275Sglebius CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__, 2028178228Smav node->nd_ID, node, item, rw ? "WRITER" : "READER" ); 2029229003Sglebius 203070700Sjulian /* 2031151973Sglebius * We can take the worklist lock with the node locked 2032151973Sglebius * BUT NOT THE REVERSE! 2033151973Sglebius */ 2034151973Sglebius if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2035178228Smav ng_worklist_add(node); 2036177953Smav NG_QUEUE_UNLOCK(ngq); 203770700Sjulian} 203852419Sjulian 2039178039Smav/* Acquire reader lock on node. If node is busy, queue the packet. */ 204070700Sjulianstatic __inline item_p 2041178228Smavng_acquire_read(node_p node, item_p item) 204252419Sjulian{ 2043178228Smav KASSERT(node != &ng_deadnode, 2044151974Sglebius ("%s: working on deadnode", __func__)); 204552419Sjulian 2046177953Smav /* Reader needs node without writer and pending items. */ 2047229003Sglebius for (;;) { 2048178228Smav long t = node->nd_input_queue.q_flags; 2049177953Smav if (t & NGQ_RMASK) 2050177953Smav break; /* Node is not ready for reader. */ 2051229003Sglebius if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, t, 2052229003Sglebius t + READER_INCREMENT)) { 2053177953Smav /* Successfully grabbed node */ 2054177953Smav CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", 2055178228Smav __func__, node->nd_ID, node, item); 2056177953Smav return (item); 2057177953Smav } 2058177953Smav cpu_spinwait(); 2059297793Spfg } 206070700Sjulian 2061177953Smav /* Queue the request for later. */ 2062178228Smav ng_queue_rw(node, item, NGQRW_R); 206370700Sjulian 2064148236Sglebius return (NULL); 206570700Sjulian} 206670700Sjulian 2067178039Smav/* Acquire writer lock on node. If node is busy, queue the packet. */ 206870700Sjulianstatic __inline item_p 2069178228Smavng_acquire_write(node_p node, item_p item) 207070700Sjulian{ 2071178228Smav KASSERT(node != &ng_deadnode, 2072151974Sglebius ("%s: working on deadnode", __func__)); 2073151974Sglebius 2074177953Smav /* Writer needs completely idle node. */ 2075229003Sglebius if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, 0, 2076229003Sglebius WRITER_ACTIVE)) { 2077177953Smav /* Successfully grabbed node */ 2078154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", 2079178228Smav __func__, node->nd_ID, node, item); 208070700Sjulian return (item); 208152419Sjulian } 208252419Sjulian 2083177953Smav /* Queue the request for later. */ 2084178228Smav ng_queue_rw(node, item, NGQRW_W); 208570700Sjulian 2086148236Sglebius return (NULL); 208770700Sjulian} 208870700Sjulian 2089167385Sjulian#if 0 2090167385Sjulianstatic __inline item_p 2091178228Smavng_upgrade_write(node_p node, item_p item) 2092167385Sjulian{ 2093178228Smav struct ng_queue *ngq = &node->nd_input_queue; 2094178228Smav KASSERT(node != &ng_deadnode, 2095167385Sjulian ("%s: working on deadnode", __func__)); 2096167385Sjulian 2097167385Sjulian NGI_SET_WRITER(item); 2098167385Sjulian 2099178228Smav NG_QUEUE_LOCK(ngq); 2100167385Sjulian 2101167385Sjulian /* 2102167385Sjulian * There will never be no readers as we are there ourselves. 2103167385Sjulian * Set the WRITER_ACTIVE flags ASAP to block out fast track readers. 2104167385Sjulian * The caller we are running from will call ng_leave_read() 2105167385Sjulian * soon, so we must account for that. We must leave again with the 2106167385Sjulian * READER lock. If we find other readers, then 2107167385Sjulian * queue the request for later. However "later" may be rignt now 2108167385Sjulian * if there are no readers. We don't really care if there are queued 2109167385Sjulian * items as we will bypass them anyhow. 2110167385Sjulian */ 2111178228Smav atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT); 2112178228Smav if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) { 2113178228Smav NG_QUEUE_UNLOCK(ngq); 2114167385Sjulian 2115167385Sjulian /* It's just us, act on the item. */ 2116167385Sjulian /* will NOT drop writer lock when done */ 2117167385Sjulian ng_apply_item(node, item, 0); 2118167385Sjulian 2119167385Sjulian /* 2120229003Sglebius * Having acted on the item, atomically 2121229003Sglebius * downgrade back to READER and finish up. 2122167385Sjulian */ 2123229003Sglebius atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE); 2124167385Sjulian 2125167385Sjulian /* Our caller will call ng_leave_read() */ 2126167385Sjulian return; 2127167385Sjulian } 2128167385Sjulian /* 2129167385Sjulian * It's not just us active, so queue us AT THE HEAD. 2130167385Sjulian * "Why?" I hear you ask. 2131167385Sjulian * Put us at the head of the queue as we've already been 2132167385Sjulian * through it once. If there is nothing else waiting, 2133167385Sjulian * set the correct flags. 2134167385Sjulian */ 2135178228Smav if (STAILQ_EMPTY(&ngq->queue)) { 2136167385Sjulian /* We've gone from, 0 to 1 item in the queue */ 2137178228Smav atomic_set_int(&ngq->q_flags, OP_PENDING); 2138167385Sjulian 2139167385Sjulian CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__, 2140178228Smav node->nd_ID, node); 2141167385Sjulian }; 2142178228Smav STAILQ_INSERT_HEAD(&ngq->queue, item, el_next); 2143178228Smav CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER", 2144178228Smav __func__, node->nd_ID, node, item ); 2145167385Sjulian 2146167385Sjulian /* Reverse what we did above. That downgrades us back to reader */ 2147178228Smav atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE); 2148177953Smav if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2149178228Smav ng_worklist_add(node); 2150178228Smav NG_QUEUE_UNLOCK(ngq); 2151167385Sjulian 2152167385Sjulian return; 2153167385Sjulian} 2154167385Sjulian#endif 2155167385Sjulian 2156178039Smav/* Release reader lock. */ 215770700Sjulianstatic __inline void 2158178228Smavng_leave_read(node_p node) 215970700Sjulian{ 2160178228Smav atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT); 216170700Sjulian} 216270700Sjulian 2163178039Smav/* Release writer lock. */ 216470700Sjulianstatic __inline void 2165178228Smavng_leave_write(node_p node) 216670700Sjulian{ 2167178228Smav atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE); 216870700Sjulian} 216970700Sjulian 2170178039Smav/* Purge node queue. Called on node shutdown. */ 217170700Sjulianstatic void 2172178228Smavng_flush_input_queue(node_p node) 217370700Sjulian{ 2174178228Smav struct ng_queue *ngq = &node->nd_input_queue; 217570700Sjulian item_p item; 2176151973Sglebius 2177168049Swkoszek NG_QUEUE_LOCK(ngq); 2178178228Smav while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) { 2179178228Smav STAILQ_REMOVE_HEAD(&ngq->queue, el_next); 2180178228Smav if (STAILQ_EMPTY(&ngq->queue)) 2181178228Smav atomic_clear_int(&ngq->q_flags, OP_PENDING); 2182168049Swkoszek NG_QUEUE_UNLOCK(ngq); 218370700Sjulian 2184151973Sglebius /* If the item is supplying a callback, call it with an error */ 2185177071Smav if (item->apply != NULL) { 2186177071Smav if (item->depth == 1) 2187177071Smav item->apply->error = ENOENT; 2188177071Smav if (refcount_release(&item->apply->refs)) { 2189177071Smav (*item->apply->apply)(item->apply->context, 2190177071Smav item->apply->error); 2191177071Smav } 2192151283Sglebius } 2193132369Sjulian NG_FREE_ITEM(item); 2194168049Swkoszek NG_QUEUE_LOCK(ngq); 219570700Sjulian } 2196168049Swkoszek NG_QUEUE_UNLOCK(ngq); 219770700Sjulian} 219870700Sjulian 219970700Sjulian/*********************************************************************** 220070700Sjulian* Externally visible method for sending or queueing messages or data. 220170700Sjulian***********************************************************************/ 220270700Sjulian 220370700Sjulian/* 220471849Sjulian * The module code should have filled out the item correctly by this stage: 220570700Sjulian * Common: 220670700Sjulian * reference to destination node. 220770700Sjulian * Reference to destination rcv hook if relevant. 2208172806Smav * apply pointer must be or NULL or reference valid struct ng_apply_info. 220970700Sjulian * Data: 221070700Sjulian * pointer to mbuf 221170700Sjulian * Control_Message: 221270700Sjulian * pointer to msg. 221370700Sjulian * ID of original sender node. (return address) 221471849Sjulian * Function: 221571849Sjulian * Function pointer 221671849Sjulian * void * argument 221771849Sjulian * integer argument 221870700Sjulian * 221970700Sjulian * The nodes have several routines and macros to help with this task: 222070700Sjulian */ 222170700Sjulian 222270700Sjulianint 2223146281Sglebiusng_snd_item(item_p item, int flags) 222470700Sjulian{ 2225172806Smav hook_p hook; 2226172806Smav node_p node; 2227146281Sglebius int queue, rw; 2228172806Smav struct ng_queue *ngq; 2229151256Sglebius int error = 0; 223070700Sjulian 2231176046Smav /* We are sending item, so it must be present! */ 2232176046Smav KASSERT(item != NULL, ("ng_snd_item: item is NULL")); 2233172806Smav 223470784Sjulian#ifdef NETGRAPH_DEBUG 2235152451Sglebius _ngi_check(item, __FILE__, __LINE__); 223670700Sjulian#endif 223770700Sjulian 2238176046Smav /* Item was sent once more, postpone apply() call. */ 2239172806Smav if (item->apply) 2240172806Smav refcount_acquire(&item->apply->refs); 2241146281Sglebius 2242172806Smav node = NGI_NODE(item); 2243176046Smav /* Node is never optional. */ 2244176046Smav KASSERT(node != NULL, ("ng_snd_item: node is NULL")); 2245172806Smav 2246175850Smav hook = NGI_HOOK(item); 2247176046Smav /* Valid hook and mbuf are mandatory for data. */ 2248176046Smav if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) { 2249176046Smav KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL")); 2250131123Sjulian if (NGI_M(item) == NULL) 2251172806Smav ERROUT(EINVAL); 225270700Sjulian CHECK_DATA_MBUF(NGI_M(item)); 225369922Sjulian } 2254149505Sglebius 225570700Sjulian /* 2256176046Smav * If the item or the node specifies single threading, force 2257176046Smav * writer semantics. Similarly, the node may say one hook always 2258176046Smav * produces writers. These are overrides. 225970700Sjulian */ 2260176567Smav if (((item->el_flags & NGQF_RW) == NGQF_WRITER) || 2261176046Smav (node->nd_flags & NGF_FORCE_WRITER) || 2262176046Smav (hook && (hook->hk_flags & HK_FORCE_WRITER))) { 2263176046Smav rw = NGQRW_W; 2264176046Smav } else { 2265176046Smav rw = NGQRW_R; 2266176046Smav } 2267149505Sglebius 2268175847Smav /* 2269194012Szec * If sender or receiver requests queued delivery, or call graph 2270194012Szec * loops back from outbound to inbound path, or stack usage 2271175847Smav * level is dangerous - enqueue message. 2272175847Smav */ 2273175847Smav if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) { 2274175847Smav queue = 1; 2275194012Szec } else if (hook && (hook->hk_flags & HK_TO_INBOUND) && 2276194012Szec curthread->td_ng_outbound) { 2277194012Szec queue = 1; 2278176046Smav } else { 2279176046Smav queue = 0; 2280175847Smav#ifdef GET_STACK_USAGE 2281175868Smav /* 2282175871Smarck * Most of netgraph nodes have small stack consumption and 2283176046Smav * for them 25% of free stack space is more than enough. 2284175868Smav * Nodes/hooks with higher stack usage should be marked as 2285175889Smarck * HI_STACK. For them 50% of stack will be guaranteed then. 2286176046Smav * XXX: Values 25% and 50% are completely empirical. 2287175868Smav */ 2288176046Smav size_t st, su, sl; 2289175847Smav GET_STACK_USAGE(st, su); 2290176046Smav sl = st - su; 2291229003Sglebius if ((sl * 4 < st) || ((sl * 2 < st) && 2292229003Sglebius ((node->nd_flags & NGF_HI_STACK) || (hook && 2293229003Sglebius (hook->hk_flags & HK_HI_STACK))))) 2294175847Smav queue = 1; 2295176046Smav#endif 2296175847Smav } 2297175847Smav 229870700Sjulian if (queue) { 229970700Sjulian /* Put it on the queue for that node*/ 2300178228Smav ng_queue_rw(node, item, rw); 2301176046Smav return ((flags & NG_PROGRESS) ? EINPROGRESS : 0); 230270700Sjulian } 230369922Sjulian 230470700Sjulian /* 230570700Sjulian * We already decided how we will be queueud or treated. 230670700Sjulian * Try get the appropriate operating permission. 230770700Sjulian */ 2308151256Sglebius if (rw == NGQRW_R) 2309178228Smav item = ng_acquire_read(node, item); 2310151256Sglebius else 2311178228Smav item = ng_acquire_write(node, item); 231252419Sjulian 2313176046Smav /* Item was queued while trying to get permission. */ 2314176046Smav if (item == NULL) 2315176046Smav return ((flags & NG_PROGRESS) ? EINPROGRESS : 0); 231652419Sjulian 231774078Sjulian NGI_GET_NODE(item, node); /* zaps stored node */ 231852419Sjulian 2319177071Smav item->depth++; 2320170180Sglebius error = ng_apply_item(node, item, rw); /* drops r/w lock when done */ 232174078Sjulian 2322177953Smav /* If something is waiting on queue and ready, schedule it. */ 2323178228Smav ngq = &node->nd_input_queue; 2324177953Smav if (QUEUE_ACTIVE(ngq)) { 2325177953Smav NG_QUEUE_LOCK(ngq); 2326177953Smav if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2327178228Smav ng_worklist_add(node); 2328177953Smav NG_QUEUE_UNLOCK(ngq); 2329177953Smav } 2330177953Smav 233152419Sjulian /* 2332177953Smav * Node may go away as soon as we remove the reference. 2333177953Smav * Whatever we do, DO NOT access the node again! 233474078Sjulian */ 2335177953Smav NG_NODE_UNREF(node); 233674078Sjulian 233771849Sjulian return (error); 2338172806Smav 2339172806Smavdone: 2340176046Smav /* If was not sent, apply callback here. */ 2341177071Smav if (item->apply != NULL) { 2342177071Smav if (item->depth == 0 && error != 0) 2343177071Smav item->apply->error = error; 2344177071Smav if (refcount_release(&item->apply->refs)) { 2345177071Smav (*item->apply->apply)(item->apply->context, 2346177071Smav item->apply->error); 2347177071Smav } 2348177071Smav } 2349175850Smav 2350172806Smav NG_FREE_ITEM(item); 2351172806Smav return (error); 235252419Sjulian} 235352419Sjulian 235452419Sjulian/* 235570700Sjulian * We have an item that was possibly queued somewhere. 235670700Sjulian * It should contain all the information needed 235770700Sjulian * to run it on the appropriate node/hook. 2358172806Smav * If there is apply pointer and we own the last reference, call apply(). 235952419Sjulian */ 2360170180Sglebiusstatic int 2361151238Sglebiusng_apply_item(node_p node, item_p item, int rw) 236252419Sjulian{ 236370700Sjulian hook_p hook; 236470700Sjulian ng_rcvdata_t *rcvdata; 236571885Sjulian ng_rcvmsg_t *rcvmsg; 2366172806Smav struct ng_apply_info *apply; 2367177071Smav int error = 0, depth; 236852419Sjulian 2369176046Smav /* Node and item are never optional. */ 2370176046Smav KASSERT(node != NULL, ("ng_apply_item: node is NULL")); 2371176046Smav KASSERT(item != NULL, ("ng_apply_item: item is NULL")); 2372176046Smav 237371849Sjulian NGI_GET_HOOK(item, hook); /* clears stored hook */ 237470784Sjulian#ifdef NETGRAPH_DEBUG 2375152451Sglebius _ngi_check(item, __FILE__, __LINE__); 237670700Sjulian#endif 2377147774Sglebius 2378172806Smav apply = item->apply; 2379177071Smav depth = item->depth; 2380147774Sglebius 238171047Sjulian switch (item->el_flags & NGQF_TYPE) { 238270700Sjulian case NGQF_DATA: 238370700Sjulian /* 238470700Sjulian * Check things are still ok as when we were queued. 238570700Sjulian */ 2386176046Smav KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL")); 2387176046Smav if (NG_HOOK_NOT_VALID(hook) || 2388176046Smav NG_NODE_NOT_VALID(node)) { 2389167402Sjulian error = EIO; 239070700Sjulian NG_FREE_ITEM(item); 239171885Sjulian break; 239270700Sjulian } 239371885Sjulian /* 239471885Sjulian * If no receive method, just silently drop it. 2395229003Sglebius * Give preference to the hook over-ride method. 239671885Sjulian */ 2397229003Sglebius if ((!(rcvdata = hook->hk_rcvdata)) && 2398229003Sglebius (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) { 239971885Sjulian error = 0; 240071885Sjulian NG_FREE_ITEM(item); 240171885Sjulian break; 240271885Sjulian } 2403167402Sjulian error = (*rcvdata)(hook, item); 240470700Sjulian break; 240570700Sjulian case NGQF_MESG: 2406175850Smav if (hook && NG_HOOK_NOT_VALID(hook)) { 2407175850Smav /* 2408175850Smav * The hook has been zapped then we can't use it. 2409175850Smav * Immediately drop its reference. 2410175850Smav * The message may not need it. 2411175850Smav */ 2412175850Smav NG_HOOK_UNREF(hook); 2413175850Smav hook = NULL; 241470700Sjulian } 241570700Sjulian /* 241670700Sjulian * Similarly, if the node is a zombie there is 241770700Sjulian * nothing we can do with it, drop everything. 241870700Sjulian */ 241970784Sjulian if (NG_NODE_NOT_VALID(node)) { 242071047Sjulian TRAP_ERROR(); 2421167402Sjulian error = EINVAL; 242270700Sjulian NG_FREE_ITEM(item); 2423175850Smav break; 242470700Sjulian } 2425175850Smav /* 2426175850Smav * Call the appropriate message handler for the object. 2427175850Smav * It is up to the message handler to free the message. 2428175850Smav * If it's a generic message, handle it generically, 2429175850Smav * otherwise call the type's message handler (if it exists). 2430175850Smav * XXX (race). Remember that a queued message may 2431175850Smav * reference a node or hook that has just been 2432175850Smav * invalidated. It will exist as the queue code 2433175850Smav * is holding a reference, but.. 2434175850Smav */ 2435175850Smav if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) && 2436175850Smav ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) { 2437175850Smav error = ng_generic_msg(node, item, hook); 2438175850Smav break; 2439175850Smav } 2440175850Smav if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) && 2441175850Smav (!(rcvmsg = node->nd_type->rcvmsg))) { 2442175850Smav TRAP_ERROR(); 2443175850Smav error = 0; 2444175850Smav NG_FREE_ITEM(item); 2445175850Smav break; 2446175850Smav } 2447175850Smav error = (*rcvmsg)(node, item, hook); 244870700Sjulian break; 244971047Sjulian case NGQF_FN: 2450173605Sglebius case NGQF_FN2: 245171047Sjulian /* 2452182995Smav * In the case of the shutdown message we allow it to hit 245371849Sjulian * even if the node is invalid. 245471047Sjulian */ 2455182995Smav if (NG_NODE_NOT_VALID(node) && 2456182995Smav NGI_FN(item) != &ng_rmnode) { 245771047Sjulian TRAP_ERROR(); 2458167402Sjulian error = EINVAL; 2459143384Sglebius NG_FREE_ITEM(item); 246071047Sjulian break; 246171047Sjulian } 2462182995Smav /* Same is about some internal functions and invalid hook. */ 2463182995Smav if (hook && NG_HOOK_NOT_VALID(hook) && 2464182995Smav NGI_FN2(item) != &ng_con_part2 && 2465182995Smav NGI_FN2(item) != &ng_con_part3 && 2466182995Smav NGI_FN(item) != &ng_rmhook_part2) { 2467182995Smav TRAP_ERROR(); 2468182995Smav error = EINVAL; 2469182995Smav NG_FREE_ITEM(item); 2470182995Smav break; 2471182995Smav } 2472182995Smav 2473173605Sglebius if ((item->el_flags & NGQF_TYPE) == NGQF_FN) { 2474173605Sglebius (*NGI_FN(item))(node, hook, NGI_ARG1(item), 2475173605Sglebius NGI_ARG2(item)); 2476172806Smav NG_FREE_ITEM(item); 2477173605Sglebius } else /* it is NGQF_FN2 */ 2478173605Sglebius error = (*NGI_FN2(item))(node, item, hook); 2479172806Smav break; 248070700Sjulian } 248170700Sjulian /* 248270700Sjulian * We held references on some of the resources 248370700Sjulian * that we took from the item. Now that we have 248470700Sjulian * finished doing everything, drop those references. 248570700Sjulian */ 2486175850Smav if (hook) 248770784Sjulian NG_HOOK_UNREF(hook); 248870700Sjulian 2489176046Smav if (rw == NGQRW_R) 2490178228Smav ng_leave_read(node); 2491176046Smav else 2492178228Smav ng_leave_write(node); 2493147774Sglebius 2494147774Sglebius /* Apply callback. */ 2495177071Smav if (apply != NULL) { 2496177071Smav if (depth == 1 && error != 0) 2497177071Smav apply->error = error; 2498177071Smav if (refcount_release(&apply->refs)) 2499177071Smav (*apply->apply)(apply->context, apply->error); 2500177071Smav } 2501147774Sglebius 2502170180Sglebius return (error); 250370700Sjulian} 250470700Sjulian 250570700Sjulian/*********************************************************************** 250670700Sjulian * Implement the 'generic' control messages 250770700Sjulian ***********************************************************************/ 250870700Sjulianstatic int 250970700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook) 251070700Sjulian{ 251170700Sjulian int error = 0; 251270700Sjulian struct ng_mesg *msg; 251370700Sjulian struct ng_mesg *resp = NULL; 251470700Sjulian 251570700Sjulian NGI_GET_MSG(item, msg); 251652419Sjulian if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 251771047Sjulian TRAP_ERROR(); 251870700Sjulian error = EINVAL; 251970700Sjulian goto out; 252052419Sjulian } 252152419Sjulian switch (msg->header.cmd) { 252252419Sjulian case NGM_SHUTDOWN: 252371849Sjulian ng_rmnode(here, NULL, NULL, 0); 252452419Sjulian break; 252552419Sjulian case NGM_MKPEER: 252652419Sjulian { 252752419Sjulian struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 252852419Sjulian 252952419Sjulian if (msg->header.arglen != sizeof(*mkp)) { 253071047Sjulian TRAP_ERROR(); 253170700Sjulian error = EINVAL; 253270700Sjulian break; 253352419Sjulian } 253452419Sjulian mkp->type[sizeof(mkp->type) - 1] = '\0'; 253552419Sjulian mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 253652419Sjulian mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 253752419Sjulian error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 253852419Sjulian break; 253952419Sjulian } 254052419Sjulian case NGM_CONNECT: 254152419Sjulian { 254252419Sjulian struct ngm_connect *const con = 254352419Sjulian (struct ngm_connect *) msg->data; 254452419Sjulian node_p node2; 254552419Sjulian 254652419Sjulian if (msg->header.arglen != sizeof(*con)) { 254771047Sjulian TRAP_ERROR(); 254870700Sjulian error = EINVAL; 254970700Sjulian break; 255052419Sjulian } 255152419Sjulian con->path[sizeof(con->path) - 1] = '\0'; 255252419Sjulian con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 255352419Sjulian con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 255470700Sjulian /* Don't forget we get a reference.. */ 255570700Sjulian error = ng_path2noderef(here, con->path, &node2, NULL); 255652419Sjulian if (error) 255752419Sjulian break; 2558172806Smav error = ng_con_nodes(item, here, con->ourhook, 2559172806Smav node2, con->peerhook); 256070784Sjulian NG_NODE_UNREF(node2); 256152419Sjulian break; 256252419Sjulian } 256352419Sjulian case NGM_NAME: 256452419Sjulian { 256552419Sjulian struct ngm_name *const nam = (struct ngm_name *) msg->data; 256652419Sjulian 256752419Sjulian if (msg->header.arglen != sizeof(*nam)) { 256871047Sjulian TRAP_ERROR(); 256970700Sjulian error = EINVAL; 257070700Sjulian break; 257152419Sjulian } 257252419Sjulian nam->name[sizeof(nam->name) - 1] = '\0'; 257352419Sjulian error = ng_name_node(here, nam->name); 257452419Sjulian break; 257552419Sjulian } 257652419Sjulian case NGM_RMHOOK: 257752419Sjulian { 257852419Sjulian struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 257952419Sjulian hook_p hook; 258052419Sjulian 258152419Sjulian if (msg->header.arglen != sizeof(*rmh)) { 258271047Sjulian TRAP_ERROR(); 258370700Sjulian error = EINVAL; 258470700Sjulian break; 258552419Sjulian } 258652419Sjulian rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 258754096Sarchie if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 258852419Sjulian ng_destroy_hook(hook); 258952419Sjulian break; 259052419Sjulian } 259152419Sjulian case NGM_NODEINFO: 259252419Sjulian { 259352419Sjulian struct nodeinfo *ni; 259452419Sjulian 259570700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT); 259652419Sjulian if (resp == NULL) { 259752419Sjulian error = ENOMEM; 259852419Sjulian break; 259952419Sjulian } 260052419Sjulian 260152419Sjulian /* Fill in node info */ 260270700Sjulian ni = (struct nodeinfo *) resp->data; 260370784Sjulian if (NG_NODE_HAS_NAME(here)) 2604125028Sharti strcpy(ni->name, NG_NODE_NAME(here)); 2605125028Sharti strcpy(ni->type, here->nd_type->name); 260652722Sjulian ni->id = ng_node2ID(here); 260770784Sjulian ni->hooks = here->nd_numhooks; 260852419Sjulian break; 260952419Sjulian } 261052419Sjulian case NGM_LISTHOOKS: 261152419Sjulian { 261270784Sjulian const int nhooks = here->nd_numhooks; 261352419Sjulian struct hooklist *hl; 261452419Sjulian struct nodeinfo *ni; 261552419Sjulian hook_p hook; 261652419Sjulian 261752419Sjulian /* Get response struct */ 2618229003Sglebius NG_MKRESPONSE(resp, msg, sizeof(*hl) + 2619229003Sglebius (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 262052419Sjulian if (resp == NULL) { 262152419Sjulian error = ENOMEM; 262252419Sjulian break; 262352419Sjulian } 262470700Sjulian hl = (struct hooklist *) resp->data; 262552419Sjulian ni = &hl->nodeinfo; 262652419Sjulian 262752419Sjulian /* Fill in node info */ 262870784Sjulian if (NG_NODE_HAS_NAME(here)) 2629125028Sharti strcpy(ni->name, NG_NODE_NAME(here)); 2630125028Sharti strcpy(ni->type, here->nd_type->name); 263152722Sjulian ni->id = ng_node2ID(here); 263252419Sjulian 263352419Sjulian /* Cycle through the linked list of hooks */ 263452419Sjulian ni->hooks = 0; 263570784Sjulian LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) { 263652419Sjulian struct linkinfo *const link = &hl->link[ni->hooks]; 263752419Sjulian 263852419Sjulian if (ni->hooks >= nhooks) { 263952419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 264087599Sobrien __func__, "hooks"); 264152419Sjulian break; 264252419Sjulian } 264370784Sjulian if (NG_HOOK_NOT_VALID(hook)) 264452419Sjulian continue; 2645125028Sharti strcpy(link->ourhook, NG_HOOK_NAME(hook)); 2646125028Sharti strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook)); 264770784Sjulian if (NG_PEER_NODE_NAME(hook)[0] != '\0') 2648125028Sharti strcpy(link->nodeinfo.name, 2649125028Sharti NG_PEER_NODE_NAME(hook)); 2650125028Sharti strcpy(link->nodeinfo.type, 2651125028Sharti NG_PEER_NODE(hook)->nd_type->name); 265270784Sjulian link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook)); 265370784Sjulian link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks; 265452419Sjulian ni->hooks++; 265552419Sjulian } 265652419Sjulian break; 265752419Sjulian } 265852419Sjulian 265952419Sjulian case NGM_LISTNODES: 266052419Sjulian { 266152419Sjulian struct namelist *nl; 266252419Sjulian node_p node; 2663231831Sglebius int i; 266452419Sjulian 2665231831Sglebius IDHASH_RLOCK(); 2666231831Sglebius /* Get response struct. */ 2667231831Sglebius NG_MKRESPONSE(resp, msg, sizeof(*nl) + 2668338333Smav (V_ng_nodes * sizeof(struct nodeinfo)), M_NOWAIT); 2669231831Sglebius if (resp == NULL) { 2670231831Sglebius IDHASH_RUNLOCK(); 2671231831Sglebius error = ENOMEM; 2672231831Sglebius break; 2673231831Sglebius } 2674231831Sglebius nl = (struct namelist *) resp->data; 2675231831Sglebius 2676231831Sglebius /* Cycle through the lists of nodes. */ 2677231831Sglebius nl->numnames = 0; 2678231831Sglebius for (i = 0; i <= V_ng_ID_hmask; i++) { 2679231831Sglebius LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) { 2680231831Sglebius struct nodeinfo *const np = 2681231831Sglebius &nl->nodeinfo[nl->numnames]; 2682231831Sglebius 2683231831Sglebius if (NG_NODE_NOT_VALID(node)) 2684231831Sglebius continue; 2685231831Sglebius if (NG_NODE_HAS_NAME(node)) 2686231831Sglebius strcpy(np->name, NG_NODE_NAME(node)); 2687231831Sglebius strcpy(np->type, node->nd_type->name); 2688231831Sglebius np->id = ng_node2ID(node); 2689231831Sglebius np->hooks = node->nd_numhooks; 2690231831Sglebius KASSERT(nl->numnames < V_ng_nodes, 2691231831Sglebius ("%s: no space", __func__)); 2692231831Sglebius nl->numnames++; 269370912Sjulian } 269452419Sjulian } 2695231831Sglebius IDHASH_RUNLOCK(); 2696231831Sglebius break; 2697231831Sglebius } 2698231831Sglebius case NGM_LISTNAMES: 2699231831Sglebius { 2700231831Sglebius struct namelist *nl; 2701231831Sglebius node_p node; 2702231831Sglebius int i; 270352419Sjulian 2704231831Sglebius NAMEHASH_RLOCK(); 2705231831Sglebius /* Get response struct. */ 2706229003Sglebius NG_MKRESPONSE(resp, msg, sizeof(*nl) + 2707231831Sglebius (V_ng_named_nodes * sizeof(struct nodeinfo)), M_NOWAIT); 270852419Sjulian if (resp == NULL) { 2709230480Sglebius NAMEHASH_RUNLOCK(); 271052419Sjulian error = ENOMEM; 271152419Sjulian break; 271252419Sjulian } 271370700Sjulian nl = (struct namelist *) resp->data; 271452419Sjulian 2715231831Sglebius /* Cycle through the lists of nodes. */ 271652419Sjulian nl->numnames = 0; 2717231831Sglebius for (i = 0; i <= V_ng_name_hmask; i++) { 2718181803Sbz LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) { 2719176802Smav struct nodeinfo *const np = 2720176802Smav &nl->nodeinfo[nl->numnames]; 272152419Sjulian 2722176802Smav if (NG_NODE_NOT_VALID(node)) 2723176802Smav continue; 2724231831Sglebius strcpy(np->name, NG_NODE_NAME(node)); 2725176802Smav strcpy(np->type, node->nd_type->name); 2726176802Smav np->id = ng_node2ID(node); 2727176802Smav np->hooks = node->nd_numhooks; 2728231831Sglebius KASSERT(nl->numnames < V_ng_named_nodes, 2729231831Sglebius ("%s: no space", __func__)); 2730176802Smav nl->numnames++; 273152419Sjulian } 273252419Sjulian } 2733230480Sglebius NAMEHASH_RUNLOCK(); 273452419Sjulian break; 273552419Sjulian } 273652419Sjulian 273752419Sjulian case NGM_LISTTYPES: 273852419Sjulian { 273952419Sjulian struct typelist *tl; 274052419Sjulian struct ng_type *type; 274152419Sjulian int num = 0; 274252419Sjulian 2743230480Sglebius TYPELIST_RLOCK(); 274452419Sjulian /* Count number of types */ 2745230480Sglebius LIST_FOREACH(type, &ng_typelist, types) 274652419Sjulian num++; 274752419Sjulian 274852419Sjulian /* Get response struct */ 2749229003Sglebius NG_MKRESPONSE(resp, msg, sizeof(*tl) + 2750229003Sglebius (num * sizeof(struct typeinfo)), M_NOWAIT); 275152419Sjulian if (resp == NULL) { 2752230480Sglebius TYPELIST_RUNLOCK(); 275352419Sjulian error = ENOMEM; 275452419Sjulian break; 275552419Sjulian } 275670700Sjulian tl = (struct typelist *) resp->data; 275752419Sjulian 275852419Sjulian /* Cycle through the linked list of types */ 275952419Sjulian tl->numtypes = 0; 276070700Sjulian LIST_FOREACH(type, &ng_typelist, types) { 276152419Sjulian struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 276252419Sjulian 2763125028Sharti strcpy(tp->type_name, type->name); 276471603Sjulian tp->numnodes = type->refs - 1; /* don't count list */ 2765230480Sglebius KASSERT(tl->numtypes < num, ("%s: no space", __func__)); 276652419Sjulian tl->numtypes++; 276752419Sjulian } 2768230480Sglebius TYPELIST_RUNLOCK(); 276952419Sjulian break; 277052419Sjulian } 277152419Sjulian 277253913Sarchie case NGM_BINARY2ASCII: 277353913Sarchie { 277464510Sarchie int bufSize = 20 * 1024; /* XXX hard coded constant */ 277553913Sarchie const struct ng_parse_type *argstype; 277653913Sarchie const struct ng_cmdlist *c; 277770700Sjulian struct ng_mesg *binary, *ascii; 277853913Sarchie 277953913Sarchie /* Data area must contain a valid netgraph message */ 278053913Sarchie binary = (struct ng_mesg *)msg->data; 2781152451Sglebius if (msg->header.arglen < sizeof(struct ng_mesg) || 2782152451Sglebius (msg->header.arglen - sizeof(struct ng_mesg) < 2783152451Sglebius binary->header.arglen)) { 278471047Sjulian TRAP_ERROR(); 278553913Sarchie error = EINVAL; 278653913Sarchie break; 278753913Sarchie } 278853913Sarchie 278970700Sjulian /* Get a response message with lots of room */ 279070700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 279170159Sjulian if (resp == NULL) { 279253913Sarchie error = ENOMEM; 279353913Sarchie break; 279453913Sarchie } 279570700Sjulian ascii = (struct ng_mesg *)resp->data; 279653913Sarchie 279753913Sarchie /* Copy binary message header to response message payload */ 279853913Sarchie bcopy(binary, ascii, sizeof(*binary)); 279953913Sarchie 280053913Sarchie /* Find command by matching typecookie and command number */ 2801229003Sglebius for (c = here->nd_type->cmdlist; c != NULL && c->name != NULL; 2802229003Sglebius c++) { 2803229003Sglebius if (binary->header.typecookie == c->cookie && 2804229003Sglebius binary->header.cmd == c->cmd) 280553913Sarchie break; 280653913Sarchie } 280753913Sarchie if (c == NULL || c->name == NULL) { 280853913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 2809229003Sglebius if (binary->header.typecookie == c->cookie && 2810229003Sglebius binary->header.cmd == c->cmd) 281153913Sarchie break; 281253913Sarchie } 281353913Sarchie if (c->name == NULL) { 281470700Sjulian NG_FREE_MSG(resp); 281553913Sarchie error = ENOSYS; 281653913Sarchie break; 281753913Sarchie } 281853913Sarchie } 281953913Sarchie 282053913Sarchie /* Convert command name to ASCII */ 282153913Sarchie snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 282253913Sarchie "%s", c->name); 282353913Sarchie 282453913Sarchie /* Convert command arguments to ASCII */ 282553913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 282653913Sarchie c->respType : c->mesgType; 282770912Sjulian if (argstype == NULL) { 282853913Sarchie *ascii->data = '\0'; 282970912Sjulian } else { 283053913Sarchie if ((error = ng_unparse(argstype, 283153913Sarchie (u_char *)binary->data, 283253913Sarchie ascii->data, bufSize)) != 0) { 283370700Sjulian NG_FREE_MSG(resp); 283453913Sarchie break; 283553913Sarchie } 283653913Sarchie } 283753913Sarchie 283853913Sarchie /* Return the result as struct ng_mesg plus ASCII string */ 283953913Sarchie bufSize = strlen(ascii->data) + 1; 284053913Sarchie ascii->header.arglen = bufSize; 284170700Sjulian resp->header.arglen = sizeof(*ascii) + bufSize; 284253913Sarchie break; 284353913Sarchie } 284453913Sarchie 284553913Sarchie case NGM_ASCII2BINARY: 284653913Sarchie { 2847208036Szec int bufSize = 20 * 1024; /* XXX hard coded constant */ 284853913Sarchie const struct ng_cmdlist *c; 284953913Sarchie const struct ng_parse_type *argstype; 285070700Sjulian struct ng_mesg *ascii, *binary; 285159178Sarchie int off = 0; 285253913Sarchie 285353913Sarchie /* Data area must contain at least a struct ng_mesg + '\0' */ 285453913Sarchie ascii = (struct ng_mesg *)msg->data; 2855152451Sglebius if ((msg->header.arglen < sizeof(*ascii) + 1) || 2856152451Sglebius (ascii->header.arglen < 1) || 2857152451Sglebius (msg->header.arglen < sizeof(*ascii) + 2858152451Sglebius ascii->header.arglen)) { 285971047Sjulian TRAP_ERROR(); 286053913Sarchie error = EINVAL; 286153913Sarchie break; 286253913Sarchie } 286353913Sarchie ascii->data[ascii->header.arglen - 1] = '\0'; 286453913Sarchie 286570700Sjulian /* Get a response message with lots of room */ 286670700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 286770159Sjulian if (resp == NULL) { 286853913Sarchie error = ENOMEM; 286953913Sarchie break; 287053913Sarchie } 287170700Sjulian binary = (struct ng_mesg *)resp->data; 287253913Sarchie 287353913Sarchie /* Copy ASCII message header to response message payload */ 287453913Sarchie bcopy(ascii, binary, sizeof(*ascii)); 287553913Sarchie 287653913Sarchie /* Find command by matching ASCII command string */ 287770784Sjulian for (c = here->nd_type->cmdlist; 287853913Sarchie c != NULL && c->name != NULL; c++) { 287953913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 288053913Sarchie break; 288153913Sarchie } 288253913Sarchie if (c == NULL || c->name == NULL) { 288353913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 288453913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 288553913Sarchie break; 288653913Sarchie } 288753913Sarchie if (c->name == NULL) { 288870700Sjulian NG_FREE_MSG(resp); 288953913Sarchie error = ENOSYS; 289053913Sarchie break; 289153913Sarchie } 289253913Sarchie } 289353913Sarchie 289453913Sarchie /* Convert command name to binary */ 289553913Sarchie binary->header.cmd = c->cmd; 289653913Sarchie binary->header.typecookie = c->cookie; 289753913Sarchie 289853913Sarchie /* Convert command arguments to binary */ 289953913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 290053913Sarchie c->respType : c->mesgType; 290170912Sjulian if (argstype == NULL) { 290253913Sarchie bufSize = 0; 290370912Sjulian } else { 2904229003Sglebius if ((error = ng_parse(argstype, ascii->data, &off, 2905229003Sglebius (u_char *)binary->data, &bufSize)) != 0) { 290670700Sjulian NG_FREE_MSG(resp); 290753913Sarchie break; 290853913Sarchie } 290953913Sarchie } 291053913Sarchie 291153913Sarchie /* Return the result */ 291253913Sarchie binary->header.arglen = bufSize; 291370700Sjulian resp->header.arglen = sizeof(*binary) + bufSize; 291453913Sarchie break; 291553913Sarchie } 291653913Sarchie 291762471Sphk case NGM_TEXT_CONFIG: 291852419Sjulian case NGM_TEXT_STATUS: 291952419Sjulian /* 292052419Sjulian * This one is tricky as it passes the command down to the 292152419Sjulian * actual node, even though it is a generic type command. 292270700Sjulian * This means we must assume that the item/msg is already freed 292352419Sjulian * when control passes back to us. 292452419Sjulian */ 292570784Sjulian if (here->nd_type->rcvmsg != NULL) { 292670700Sjulian NGI_MSG(item) = msg; /* put it back as we found it */ 292770784Sjulian return((*here->nd_type->rcvmsg)(here, item, lasthook)); 292852419Sjulian } 292952419Sjulian /* Fall through if rcvmsg not supported */ 293052419Sjulian default: 293171047Sjulian TRAP_ERROR(); 293252419Sjulian error = EINVAL; 293352419Sjulian } 293470700Sjulian /* 293570700Sjulian * Sometimes a generic message may be statically allocated 2936229003Sglebius * to avoid problems with allocating when in tight memory situations. 293770700Sjulian * Don't free it if it is so. 2938298813Spfg * I break them apart here, because erros may cause a free if the item 293970700Sjulian * in which case we'd be doing it twice. 294070700Sjulian * they are kept together above, to simplify freeing. 294170700Sjulian */ 294270700Sjulianout: 294370700Sjulian NG_RESPOND_MSG(error, here, item, resp); 2944185179Smav NG_FREE_MSG(msg); 294552419Sjulian return (error); 294652419Sjulian} 294752419Sjulian 294852419Sjulian/************************************************************************ 2949146213Sglebius Queue element get/free routines 2950146213Sglebius************************************************************************/ 2951146213Sglebius 2952146213Sglebiusuma_zone_t ng_qzone; 2953178259Smavuma_zone_t ng_qdzone; 2954186093Smavstatic int numthreads = 0; /* number of queue threads */ 2955176849Smavstatic int maxalloc = 4096;/* limit the damage of a leak */ 2956278640Sglebiusstatic int maxdata = 4096; /* limit the damage of a DoS */ 2957146213Sglebius 2958186093SmavSYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads, 2959186093Smav 0, "Number of queue processing threads"); 2960146213SglebiusSYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc, 2961178259Smav 0, "Maximum number of non-data queue items to allocate"); 2962178259SmavSYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata, 2963178259Smav 0, "Maximum number of data queue items to allocate"); 2964146213Sglebius 2965146213Sglebius#ifdef NETGRAPH_DEBUG 2966146213Sglebiusstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist); 2967229003Sglebiusstatic int allocated; /* number of items malloc'd */ 2968146213Sglebius#endif 2969146213Sglebius 2970146213Sglebius/* 2971146213Sglebius * Get a queue entry. 2972146213Sglebius * This is usually called when a packet first enters netgraph. 2973146213Sglebius * By definition, this is usually from an interrupt, or from a user. 2974146213Sglebius * Users are not so important, but try be quick for the times that it's 2975146213Sglebius * an interrupt. 2976146213Sglebius */ 2977146213Sglebiusstatic __inline item_p 2978178259Smavng_alloc_item(int type, int flags) 2979146213Sglebius{ 2980178259Smav item_p item; 2981146213Sglebius 2982178259Smav KASSERT(((type & ~NGQF_TYPE) == 0), 2983178259Smav ("%s: incorrect item type: %d", __func__, type)); 2984146213Sglebius 2985229003Sglebius item = uma_zalloc((type == NGQF_DATA) ? ng_qdzone : ng_qzone, 2986178259Smav ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO); 2987146281Sglebius 2988178259Smav if (item) { 2989178259Smav item->el_flags = type; 2990146213Sglebius#ifdef NETGRAPH_DEBUG 2991178259Smav mtx_lock(&ngq_mtx); 2992178259Smav TAILQ_INSERT_TAIL(&ng_itemlist, item, all); 2993178259Smav allocated++; 2994178259Smav mtx_unlock(&ngq_mtx); 2995178259Smav#endif 2996146213Sglebius } 2997146213Sglebius 2998146213Sglebius return (item); 2999146213Sglebius} 3000146213Sglebius 3001146213Sglebius/* 3002146213Sglebius * Release a queue entry 3003146213Sglebius */ 3004146213Sglebiusvoid 3005146213Sglebiusng_free_item(item_p item) 3006146213Sglebius{ 3007146213Sglebius /* 3008146213Sglebius * The item may hold resources on it's own. We need to free 3009146213Sglebius * these before we can free the item. What they are depends upon 3010146213Sglebius * what kind of item it is. it is important that nodes zero 3011146213Sglebius * out pointers to resources that they remove from the item 3012146213Sglebius * or we release them again here. 3013146213Sglebius */ 3014146213Sglebius switch (item->el_flags & NGQF_TYPE) { 3015146213Sglebius case NGQF_DATA: 3016146213Sglebius /* If we have an mbuf still attached.. */ 3017146213Sglebius NG_FREE_M(_NGI_M(item)); 3018146213Sglebius break; 3019146213Sglebius case NGQF_MESG: 3020146213Sglebius _NGI_RETADDR(item) = 0; 3021146213Sglebius NG_FREE_MSG(_NGI_MSG(item)); 3022146213Sglebius break; 3023146213Sglebius case NGQF_FN: 3024172806Smav case NGQF_FN2: 3025146213Sglebius /* nothing to free really, */ 3026146213Sglebius _NGI_FN(item) = NULL; 3027146213Sglebius _NGI_ARG1(item) = NULL; 3028146213Sglebius _NGI_ARG2(item) = 0; 3029146213Sglebius break; 3030146213Sglebius } 3031146213Sglebius /* If we still have a node or hook referenced... */ 3032146213Sglebius _NGI_CLR_NODE(item); 3033146213Sglebius _NGI_CLR_HOOK(item); 3034146213Sglebius 3035146213Sglebius#ifdef NETGRAPH_DEBUG 3036146213Sglebius mtx_lock(&ngq_mtx); 3037146213Sglebius TAILQ_REMOVE(&ng_itemlist, item, all); 3038146213Sglebius allocated--; 3039146213Sglebius mtx_unlock(&ngq_mtx); 3040146213Sglebius#endif 3041229003Sglebius uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA) ? 3042229003Sglebius ng_qdzone : ng_qzone, item); 3043146213Sglebius} 3044146213Sglebius 3045178259Smav/* 3046178259Smav * Change type of the queue entry. 3047178259Smav * Possibly reallocates it from another UMA zone. 3048178259Smav */ 3049178259Smavstatic __inline item_p 3050178259Smavng_realloc_item(item_p pitem, int type, int flags) 3051178259Smav{ 3052178259Smav item_p item; 3053178259Smav int from, to; 3054178259Smav 3055178259Smav KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__)); 3056178259Smav KASSERT(((type & ~NGQF_TYPE) == 0), 3057178259Smav ("%s: incorrect item type: %d", __func__, type)); 3058178259Smav 3059178259Smav from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA); 3060178259Smav to = (type == NGQF_DATA); 3061178259Smav if (from != to) { 3062178259Smav /* If reallocation is required do it and copy item. */ 3063178259Smav if ((item = ng_alloc_item(type, flags)) == NULL) { 3064178259Smav ng_free_item(pitem); 3065178259Smav return (NULL); 3066178259Smav } 3067178259Smav *item = *pitem; 3068178259Smav ng_free_item(pitem); 3069178259Smav } else 3070178259Smav item = pitem; 3071178259Smav item->el_flags = (item->el_flags & ~NGQF_TYPE) | type; 3072178259Smav 3073178259Smav return (item); 3074178259Smav} 3075178259Smav 3076146213Sglebius/************************************************************************ 307752419Sjulian Module routines 307852419Sjulian************************************************************************/ 307952419Sjulian 308052419Sjulian/* 308152419Sjulian * Handle the loading/unloading of a netgraph node type module 308252419Sjulian */ 308352419Sjulianint 308452419Sjulianng_mod_event(module_t mod, int event, void *data) 308552419Sjulian{ 308652419Sjulian struct ng_type *const type = data; 3087229003Sglebius int error = 0; 308852419Sjulian 308952419Sjulian switch (event) { 309052419Sjulian case MOD_LOAD: 309152419Sjulian 309252419Sjulian /* Register new netgraph node type */ 3093229003Sglebius if ((error = ng_newtype(type)) != 0) 309452419Sjulian break; 309552419Sjulian 309652419Sjulian /* Call type specific code */ 309752419Sjulian if (type->mod_event != NULL) 309870700Sjulian if ((error = (*type->mod_event)(mod, event, data))) { 3099230480Sglebius TYPELIST_WLOCK(); 310071603Sjulian type->refs--; /* undo it */ 310152419Sjulian LIST_REMOVE(type, types); 3102230480Sglebius TYPELIST_WUNLOCK(); 310370700Sjulian } 310452419Sjulian break; 310552419Sjulian 310652419Sjulian case MOD_UNLOAD: 310771603Sjulian if (type->refs > 1) { /* make sure no nodes exist! */ 310852419Sjulian error = EBUSY; 310971603Sjulian } else { 3110229003Sglebius if (type->refs == 0) /* failed load, nothing to undo */ 311171603Sjulian break; 311252419Sjulian if (type->mod_event != NULL) { /* check with type */ 311352419Sjulian error = (*type->mod_event)(mod, event, data); 3114229003Sglebius if (error != 0) /* type refuses.. */ 311552419Sjulian break; 311652419Sjulian } 3117230480Sglebius TYPELIST_WLOCK(); 311852419Sjulian LIST_REMOVE(type, types); 3119230480Sglebius TYPELIST_WUNLOCK(); 312052419Sjulian } 312152419Sjulian break; 312252419Sjulian 312352419Sjulian default: 312452419Sjulian if (type->mod_event != NULL) 312552419Sjulian error = (*type->mod_event)(mod, event, data); 312652419Sjulian else 3127132199Sphk error = EOPNOTSUPP; /* XXX ? */ 312852419Sjulian break; 312952419Sjulian } 313052419Sjulian return (error); 313152419Sjulian} 313252419Sjulian 3133231831Sglebiusstatic void 3134231831Sglebiusvnet_netgraph_init(const void *unused __unused) 3135231831Sglebius{ 3136231831Sglebius 3137231831Sglebius /* We start with small hashes, but they can grow. */ 3138231831Sglebius V_ng_ID_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_ID_hmask); 3139231831Sglebius V_ng_name_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_name_hmask); 3140231831Sglebius} 3141231831SglebiusVNET_SYSINIT(vnet_netgraph_init, SI_SUB_NETGRAPH, SI_ORDER_FIRST, 3142231831Sglebius vnet_netgraph_init, NULL); 3143231831Sglebius 3144229003Sglebius#ifdef VIMAGE 3145195837Srwatsonstatic void 3146195837Srwatsonvnet_netgraph_uninit(const void *unused __unused) 3147193731Szec{ 3148207572Szec node_p node = NULL, last_killed = NULL; 3149207572Szec int i; 3150193731Szec 3151207572Szec do { 3152207572Szec /* Find a node to kill */ 3153231831Sglebius IDHASH_RLOCK(); 3154231831Sglebius for (i = 0; i <= V_ng_ID_hmask; i++) { 3155231831Sglebius LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) { 3156207572Szec if (node != &ng_deadnode) { 3157207572Szec NG_NODE_REF(node); 3158207572Szec break; 3159207572Szec } 3160207572Szec } 3161207572Szec if (node != NULL) 3162207572Szec break; 3163207572Szec } 3164231831Sglebius IDHASH_RUNLOCK(); 3165207572Szec 3166207572Szec /* Attempt to kill it only if it is a regular node */ 3167207572Szec if (node != NULL) { 3168207572Szec if (node == last_killed) { 3169207572Szec /* This should never happen */ 3170229003Sglebius printf("ng node %s needs NGF_REALLY_DIE\n", 3171229003Sglebius node->nd_name); 3172207572Szec if (node->nd_flags & NGF_REALLY_DIE) 3173207572Szec panic("ng node %s won't die", 3174207572Szec node->nd_name); 3175207572Szec node->nd_flags |= NGF_REALLY_DIE; 3176207572Szec } 3177193731Szec ng_rmnode(node, NULL, NULL, 0); 3178207572Szec NG_NODE_UNREF(node); 3179207572Szec last_killed = node; 3180193731Szec } 3181207572Szec } while (node != NULL); 3182231831Sglebius 3183231831Sglebius hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask); 3184231831Sglebius hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_ID_hmask); 3185193731Szec} 3186231830SglebiusVNET_SYSUNINIT(vnet_netgraph_uninit, SI_SUB_NETGRAPH, SI_ORDER_FIRST, 3187195837Srwatson vnet_netgraph_uninit, NULL); 3188193731Szec#endif /* VIMAGE */ 3189193731Szec 319052419Sjulian/* 319152419Sjulian * Handle loading and unloading for this code. 319252419Sjulian * The only thing we need to link into is the NETISR strucure. 319352419Sjulian */ 319452419Sjulianstatic int 319552419Sjulianngb_mod_event(module_t mod, int event, void *data) 319652419Sjulian{ 3197186093Smav struct proc *p; 3198186093Smav struct thread *td; 3199186093Smav int i, error = 0; 320052419Sjulian 320152419Sjulian switch (event) { 320252419Sjulian case MOD_LOAD: 3203146212Sglebius /* Initialize everything. */ 3204168049Swkoszek NG_WORKLIST_LOCK_INIT(); 3205230480Sglebius rw_init(&ng_typelist_lock, "netgraph types"); 3206230480Sglebius rw_init(&ng_idhash_lock, "netgraph idhash"); 3207230480Sglebius rw_init(&ng_namehash_lock, "netgraph namehash"); 3208256550Smelifaro rw_init(&ng_topo_lock, "netgraph topology mutex"); 3209146212Sglebius#ifdef NETGRAPH_DEBUG 3210176802Smav mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL, 3211176802Smav MTX_DEF); 3212146212Sglebius mtx_init(&ngq_mtx, "netgraph item list mutex", NULL, 3213123278Struckman MTX_DEF); 3214146212Sglebius#endif 3215146212Sglebius ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item), 3216231997Sglebius NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0); 3217146212Sglebius uma_zone_set_max(ng_qzone, maxalloc); 3218229003Sglebius ng_qdzone = uma_zcreate("NetGraph data items", 3219229003Sglebius sizeof(struct ng_item), NULL, NULL, NULL, NULL, 3220231997Sglebius UMA_ALIGN_CACHE, 0); 3221178259Smav uma_zone_set_max(ng_qdzone, maxdata); 3222186093Smav /* Autoconfigure number of threads. */ 3223186093Smav if (numthreads <= 0) 3224186093Smav numthreads = mp_ncpus; 3225186093Smav /* Create threads. */ 3226186093Smav p = NULL; /* start with no process */ 3227186093Smav for (i = 0; i < numthreads; i++) { 3228186093Smav if (kproc_kthread_add(ngthread, NULL, &p, &td, 3229186093Smav RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) { 3230186093Smav numthreads = i; 3231186093Smav break; 3232186093Smav } 3233186093Smav } 323452419Sjulian break; 323552419Sjulian case MOD_UNLOAD: 3236167677Srwatson /* You can't unload it because an interface may be using it. */ 323752419Sjulian error = EBUSY; 323852419Sjulian break; 323952419Sjulian default: 324052419Sjulian error = EOPNOTSUPP; 324152419Sjulian break; 324252419Sjulian } 324352419Sjulian return (error); 324452419Sjulian} 324552419Sjulian 324652419Sjulianstatic moduledata_t netgraph_mod = { 324752419Sjulian "netgraph", 324852419Sjulian ngb_mod_event, 324952419Sjulian (NULL) 325052419Sjulian}; 3251231830SglebiusDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_FIRST); 325272946SjulianSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family"); 3253273377ShselaskySYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_ABI_VERSION,""); 3254273377ShselaskySYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_VERSION, ""); 325552419Sjulian 325670784Sjulian#ifdef NETGRAPH_DEBUG 325770700Sjulianvoid 325870784Sjuliandumphook (hook_p hook, char *file, int line) 325970784Sjulian{ 326070784Sjulian printf("hook: name %s, %d refs, Last touched:\n", 326170784Sjulian _NG_HOOK_NAME(hook), hook->hk_refs); 326270784Sjulian printf(" Last active @ %s, line %d\n", 326370784Sjulian hook->lastfile, hook->lastline); 326470784Sjulian if (line) { 326570784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 3266226829Sglebius#ifdef KDB 3267226829Sglebius kdb_backtrace(); 3268226829Sglebius#endif 326970784Sjulian } 327070784Sjulian} 327170784Sjulian 327270784Sjulianvoid 327370784Sjuliandumpnode(node_p node, char *file, int line) 327470784Sjulian{ 327570784Sjulian printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n", 327671047Sjulian _NG_NODE_ID(node), node->nd_type->name, 327770784Sjulian node->nd_numhooks, node->nd_flags, 327870784Sjulian node->nd_refs, node->nd_name); 327970784Sjulian printf(" Last active @ %s, line %d\n", 328070784Sjulian node->lastfile, node->lastline); 328170784Sjulian if (line) { 328270784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 3283226829Sglebius#ifdef KDB 3284226829Sglebius kdb_backtrace(); 3285226829Sglebius#endif 328670784Sjulian } 328770784Sjulian} 328870784Sjulian 328970784Sjulianvoid 329070700Sjuliandumpitem(item_p item, char *file, int line) 329170700Sjulian{ 3292146212Sglebius printf(" ACTIVE item, last used at %s, line %d", 3293146212Sglebius item->lastfile, item->lastline); 3294146212Sglebius switch(item->el_flags & NGQF_TYPE) { 3295146212Sglebius case NGQF_DATA: 3296146212Sglebius printf(" - [data]\n"); 3297146212Sglebius break; 3298146212Sglebius case NGQF_MESG: 3299146212Sglebius printf(" - retaddr[%d]:\n", _NGI_RETADDR(item)); 3300146212Sglebius break; 3301146212Sglebius case NGQF_FN: 3302172820Sru printf(" - fn@%p (%p, %p, %p, %d (%x))\n", 3303172820Sru _NGI_FN(item), 3304172820Sru _NGI_NODE(item), 3305172820Sru _NGI_HOOK(item), 3306172820Sru item->body.fn.fn_arg1, 3307172820Sru item->body.fn.fn_arg2, 3308172820Sru item->body.fn.fn_arg2); 3309172820Sru break; 3310172806Smav case NGQF_FN2: 3311173110Smav printf(" - fn2@%p (%p, %p, %p, %d (%x))\n", 3312172820Sru _NGI_FN2(item), 3313149735Sglebius _NGI_NODE(item), 3314149735Sglebius _NGI_HOOK(item), 3315146212Sglebius item->body.fn.fn_arg1, 3316146212Sglebius item->body.fn.fn_arg2, 3317146212Sglebius item->body.fn.fn_arg2); 3318146212Sglebius break; 331952419Sjulian } 332070784Sjulian if (line) { 332170784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 3322149735Sglebius if (_NGI_NODE(item)) { 332370784Sjulian printf("node %p ([%x])\n", 3324149735Sglebius _NGI_NODE(item), ng_node2ID(_NGI_NODE(item))); 332570784Sjulian } 332670784Sjulian } 332770700Sjulian} 332852419Sjulian 332970784Sjulianstatic void 333070784Sjulianng_dumpitems(void) 333170784Sjulian{ 333270784Sjulian item_p item; 333370784Sjulian int i = 1; 333470784Sjulian TAILQ_FOREACH(item, &ng_itemlist, all) { 333570784Sjulian printf("[%d] ", i++); 333670784Sjulian dumpitem(item, NULL, 0); 333770784Sjulian } 333870784Sjulian} 333970784Sjulian 334070784Sjulianstatic void 334170784Sjulianng_dumpnodes(void) 334270784Sjulian{ 334370784Sjulian node_p node; 334470784Sjulian int i = 1; 3345131008Srwatson mtx_lock(&ng_nodelist_mtx); 334670784Sjulian SLIST_FOREACH(node, &ng_allnodes, nd_all) { 334770784Sjulian printf("[%d] ", i++); 334870784Sjulian dumpnode(node, NULL, 0); 334970784Sjulian } 3350131008Srwatson mtx_unlock(&ng_nodelist_mtx); 335170784Sjulian} 335270784Sjulian 335370784Sjulianstatic void 335470784Sjulianng_dumphooks(void) 335570784Sjulian{ 335670784Sjulian hook_p hook; 335770784Sjulian int i = 1; 3358131008Srwatson mtx_lock(&ng_nodelist_mtx); 335970784Sjulian SLIST_FOREACH(hook, &ng_allhooks, hk_all) { 336070784Sjulian printf("[%d] ", i++); 336170784Sjulian dumphook(hook, NULL, 0); 336270784Sjulian } 3363131008Srwatson mtx_unlock(&ng_nodelist_mtx); 336470784Sjulian} 336570784Sjulian 336670700Sjulianstatic int 336770700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS) 336870700Sjulian{ 336970700Sjulian int error; 337070700Sjulian int val; 337170700Sjulian int i; 337270700Sjulian 337370700Sjulian val = allocated; 337470700Sjulian i = 1; 3375170289Sdwmalone error = sysctl_handle_int(oidp, &val, 0, req); 337671047Sjulian if (error != 0 || req->newptr == NULL) 337771047Sjulian return (error); 337871047Sjulian if (val == 42) { 337970784Sjulian ng_dumpitems(); 338070784Sjulian ng_dumpnodes(); 338170784Sjulian ng_dumphooks(); 338270700Sjulian } 338371047Sjulian return (0); 338452419Sjulian} 338552419Sjulian 338671047SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW, 338771047Sjulian 0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items"); 338870784Sjulian#endif /* NETGRAPH_DEBUG */ 338970700Sjulian 339070700Sjulian/*********************************************************************** 339170700Sjulian* Worklist routines 339270700Sjulian**********************************************************************/ 339352419Sjulian/* 339470700Sjulian * Pick a node off the list of nodes with work, 3395186093Smav * try get an item to process off it. Remove the node from the list. 339652419Sjulian */ 339770700Sjulianstatic void 3398186093Smavngthread(void *arg) 339952419Sjulian{ 3400177953Smav for (;;) { 3401177953Smav node_p node; 340252419Sjulian 3403177953Smav /* Get node from the worklist. */ 3404168049Swkoszek NG_WORKLIST_LOCK(); 3405186093Smav while ((node = STAILQ_FIRST(&ng_worklist)) == NULL) 3406186093Smav NG_WORKLIST_SLEEP(); 3407178228Smav STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work); 3408168049Swkoszek NG_WORKLIST_UNLOCK(); 3409193731Szec CURVNET_SET(node->nd_vnet); 3410154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist", 3411154275Sglebius __func__, node->nd_ID, node); 341270700Sjulian /* 341370700Sjulian * We have the node. We also take over the reference 341470700Sjulian * that the list had on it. 341570700Sjulian * Now process as much as you can, until it won't 341670700Sjulian * let you have another item off the queue. 341770700Sjulian * All this time, keep the reference 341870700Sjulian * that lets us be sure that the node still exists. 341970700Sjulian * Let the reference go at the last minute. 342070700Sjulian */ 342170700Sjulian for (;;) { 3422177953Smav item_p item; 3423151238Sglebius int rw; 3424151238Sglebius 3425168049Swkoszek NG_QUEUE_LOCK(&node->nd_input_queue); 3426178228Smav item = ng_dequeue(node, &rw); 342770700Sjulian if (item == NULL) { 3428178228Smav node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ; 3429168049Swkoszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 343070700Sjulian break; /* go look for another node */ 343170700Sjulian } else { 3432168049Swkoszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 343374078Sjulian NGI_GET_NODE(item, node); /* zaps stored node */ 3434151238Sglebius ng_apply_item(node, item, rw); 343574078Sjulian NG_NODE_UNREF(node); 343670700Sjulian } 343770700Sjulian } 343873238Sjulian NG_NODE_UNREF(node); 3439193731Szec CURVNET_RESTORE(); 344052419Sjulian } 344170700Sjulian} 344269922Sjulian 344372979Sjulian/* 344472979Sjulian * XXX 344572979Sjulian * It's posible that a debugging NG_NODE_REF may need 344672979Sjulian * to be outside the mutex zone 344772979Sjulian */ 344870700Sjulianstatic void 3449177953Smavng_worklist_add(node_p node) 345070700Sjulian{ 3451148236Sglebius 3452148266Sglebius mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED); 3453148236Sglebius 3454178228Smav if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) { 345569922Sjulian /* 345670700Sjulian * If we are not already on the work queue, 345770700Sjulian * then put us on. 345869922Sjulian */ 3459178228Smav node->nd_input_queue.q_flags2 |= NGQ2_WORKQ; 3460229003Sglebius NG_NODE_REF(node); /* XXX safe in mutex? */ 3461168049Swkoszek NG_WORKLIST_LOCK(); 3462178228Smav STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work); 3463168049Swkoszek NG_WORKLIST_UNLOCK(); 3464154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__, 3465154275Sglebius node->nd_ID, node); 3466186093Smav NG_WORKLIST_WAKEUP(); 3467177953Smav } else { 3468154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist", 3469154275Sglebius __func__, node->nd_ID, node); 3470177953Smav } 347170700Sjulian} 347270700Sjulian 347370700Sjulian/*********************************************************************** 347470700Sjulian* Externally useable functions to set up a queue item ready for sending 347570700Sjulian***********************************************************************/ 347670700Sjulian 347770784Sjulian#ifdef NETGRAPH_DEBUG 347870784Sjulian#define ITEM_DEBUG_CHECKS \ 347970700Sjulian do { \ 348071849Sjulian if (NGI_NODE(item) ) { \ 348170700Sjulian printf("item already has node"); \ 3482174898Srwatson kdb_enter(KDB_WHY_NETGRAPH, "has node"); \ 348371849Sjulian NGI_CLR_NODE(item); \ 348470700Sjulian } \ 348571849Sjulian if (NGI_HOOK(item) ) { \ 348670700Sjulian printf("item already has hook"); \ 3487174898Srwatson kdb_enter(KDB_WHY_NETGRAPH, "has hook"); \ 348871849Sjulian NGI_CLR_HOOK(item); \ 348970700Sjulian } \ 349070700Sjulian } while (0) 349170700Sjulian#else 349270784Sjulian#define ITEM_DEBUG_CHECKS 349370700Sjulian#endif 349470700Sjulian 349570700Sjulian/* 3496131374Sjulian * Put mbuf into the item. 349770700Sjulian * Hook and node references will be removed when the item is dequeued. 349870700Sjulian * (or equivalent) 349970700Sjulian * (XXX) Unsafe because no reference held by peer on remote node. 350070700Sjulian * remote node might go away in this timescale. 350170700Sjulian * We know the hooks can't go away because that would require getting 350270700Sjulian * a writer item on both nodes and we must have at least a reader 3503151973Sglebius * here to be able to do this. 350470700Sjulian * Note that the hook loaded is the REMOTE hook. 350570700Sjulian * 350670700Sjulian * This is possibly in the critical path for new data. 350770700Sjulian */ 350870700Sjulianitem_p 3509146281Sglebiusng_package_data(struct mbuf *m, int flags) 351070700Sjulian{ 351170700Sjulian item_p item; 351270700Sjulian 3513178259Smav if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) { 3514176849Smav NG_FREE_M(m); 3515176849Smav return (NULL); 3516176849Smav } 351770784Sjulian ITEM_DEBUG_CHECKS; 3518178259Smav item->el_flags |= NGQF_READER; 351970700Sjulian NGI_M(item) = m; 352070700Sjulian return (item); 352170700Sjulian} 352270700Sjulian 352370700Sjulian/* 352470700Sjulian * Allocate a queue item and put items into it.. 352570700Sjulian * Evaluate the address as this will be needed to queue it and 352670700Sjulian * to work out what some of the fields should be. 352770700Sjulian * Hook and node references will be removed when the item is dequeued. 352870700Sjulian * (or equivalent) 352970700Sjulian */ 353070700Sjulianitem_p 3531146281Sglebiusng_package_msg(struct ng_mesg *msg, int flags) 353270700Sjulian{ 353370700Sjulian item_p item; 353470700Sjulian 3535178259Smav if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) { 353671849Sjulian NG_FREE_MSG(msg); 353770700Sjulian return (NULL); 353869922Sjulian } 353970784Sjulian ITEM_DEBUG_CHECKS; 3540149505Sglebius /* Messages items count as writers unless explicitly exempted. */ 3541149505Sglebius if (msg->header.cmd & NGM_READONLY) 3542178259Smav item->el_flags |= NGQF_READER; 3543149505Sglebius else 3544178259Smav item->el_flags |= NGQF_WRITER; 354570700Sjulian /* 354670700Sjulian * Set the current lasthook into the queue item 354770700Sjulian */ 354870700Sjulian NGI_MSG(item) = msg; 3549102244Sarchie NGI_RETADDR(item) = 0; 355070700Sjulian return (item); 355170700Sjulian} 355269922Sjulian 355371849Sjulian#define SET_RETADDR(item, here, retaddr) \ 355471047Sjulian do { /* Data or fn items don't have retaddrs */ \ 355571047Sjulian if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) { \ 355670700Sjulian if (retaddr) { \ 355770700Sjulian NGI_RETADDR(item) = retaddr; \ 355870700Sjulian } else { \ 355970700Sjulian /* \ 356070700Sjulian * The old return address should be ok. \ 356170700Sjulian * If there isn't one, use the address \ 356270700Sjulian * here. \ 356370700Sjulian */ \ 356470700Sjulian if (NGI_RETADDR(item) == 0) { \ 356570700Sjulian NGI_RETADDR(item) \ 356670700Sjulian = ng_node2ID(here); \ 356770700Sjulian } \ 356870700Sjulian } \ 356970700Sjulian } \ 357070700Sjulian } while (0) 357170700Sjulian 357270700Sjulianint 357370700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr) 357470700Sjulian{ 357571849Sjulian hook_p peer; 357671849Sjulian node_p peernode; 357770784Sjulian ITEM_DEBUG_CHECKS; 357870700Sjulian /* 357970700Sjulian * Quick sanity check.. 358070784Sjulian * Since a hook holds a reference on it's node, once we know 358170784Sjulian * that the peer is still connected (even if invalid,) we know 358270784Sjulian * that the peer node is present, though maybe invalid. 358370700Sjulian */ 3584256550Smelifaro TOPOLOGY_RLOCK(); 3585229003Sglebius if ((hook == NULL) || NG_HOOK_NOT_VALID(hook) || 3586178311Smav NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) || 3587178311Smav NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) { 358870700Sjulian NG_FREE_ITEM(item); 358971047Sjulian TRAP_ERROR(); 3590256550Smelifaro TOPOLOGY_RUNLOCK(); 359173083Sjulian return (ENETDOWN); 359252419Sjulian } 359352419Sjulian 359470700Sjulian /* 359570700Sjulian * Transfer our interest to the other (peer) end. 359670700Sjulian */ 359771849Sjulian NG_HOOK_REF(peer); 3598178311Smav NG_NODE_REF(peernode); 359971849Sjulian NGI_SET_HOOK(item, peer); 360071849Sjulian NGI_SET_NODE(item, peernode); 360179706Sjulian SET_RETADDR(item, here, retaddr); 3602219827Sglebius 3603256550Smelifaro TOPOLOGY_RUNLOCK(); 3604219827Sglebius 360570700Sjulian return (0); 360670700Sjulian} 360752419Sjulian 360870700Sjulianint 3609227130Sfjoeng_address_path(node_p here, item_p item, const char *address, ng_ID_t retaddr) 361070700Sjulian{ 3611152451Sglebius node_p dest = NULL; 361270700Sjulian hook_p hook = NULL; 3613152451Sglebius int error; 361470700Sjulian 361570784Sjulian ITEM_DEBUG_CHECKS; 361670700Sjulian /* 361770700Sjulian * Note that ng_path2noderef increments the reference count 361870700Sjulian * on the node for us if it finds one. So we don't have to. 361970700Sjulian */ 362070700Sjulian error = ng_path2noderef(here, address, &dest, &hook); 362170700Sjulian if (error) { 362270700Sjulian NG_FREE_ITEM(item); 362370784Sjulian return (error); 362452419Sjulian } 362571849Sjulian NGI_SET_NODE(item, dest); 3626219827Sglebius if (hook) 362771849Sjulian NGI_SET_HOOK(item, hook); 3628219827Sglebius 362971849Sjulian SET_RETADDR(item, here, retaddr); 363070700Sjulian return (0); 363170700Sjulian} 363252419Sjulian 363370700Sjulianint 363470700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr) 363570700Sjulian{ 363670700Sjulian node_p dest; 363770700Sjulian 363870784Sjulian ITEM_DEBUG_CHECKS; 363970700Sjulian /* 364070700Sjulian * Find the target node. 364170700Sjulian */ 364270700Sjulian dest = ng_ID2noderef(ID); /* GETS REFERENCE! */ 364370700Sjulian if (dest == NULL) { 364470700Sjulian NG_FREE_ITEM(item); 364571047Sjulian TRAP_ERROR(); 364670700Sjulian return(EINVAL); 364770700Sjulian } 364870700Sjulian /* Fill out the contents */ 364971849Sjulian NGI_SET_NODE(item, dest); 365071849Sjulian NGI_CLR_HOOK(item); 365171849Sjulian SET_RETADDR(item, here, retaddr); 365252419Sjulian return (0); 365352419Sjulian} 365452419Sjulian 365552419Sjulian/* 365670700Sjulian * special case to send a message to self (e.g. destroy node) 365770700Sjulian * Possibly indicate an arrival hook too. 365870700Sjulian * Useful for removing that hook :-) 365952419Sjulian */ 366070700Sjulianitem_p 366170700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg) 366252419Sjulian{ 366370700Sjulian item_p item; 366452419Sjulian 366570700Sjulian /* 366670700Sjulian * Find the target node. 366770700Sjulian * If there is a HOOK argument, then use that in preference 366870700Sjulian * to the address. 366970700Sjulian */ 3670178259Smav if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) { 367171849Sjulian NG_FREE_MSG(msg); 367270700Sjulian return (NULL); 367352419Sjulian } 367470700Sjulian 367570700Sjulian /* Fill out the contents */ 3676178259Smav item->el_flags |= NGQF_WRITER; 367770784Sjulian NG_NODE_REF(here); 367871849Sjulian NGI_SET_NODE(item, here); 367971849Sjulian if (hook) { 368070784Sjulian NG_HOOK_REF(hook); 368171849Sjulian NGI_SET_HOOK(item, hook); 368271849Sjulian } 368370700Sjulian NGI_MSG(item) = msg; 368470700Sjulian NGI_RETADDR(item) = ng_node2ID(here); 368570700Sjulian return (item); 368652419Sjulian} 368752419Sjulian 3688172806Smav/* 3689172806Smav * Send ng_item_fn function call to the specified node. 3690172806Smav */ 3691172806Smav 3692146281Sglebiusint 3693173605Sglebiusng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2) 369471047Sjulian{ 369571047Sjulian 3696173605Sglebius return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS); 369771047Sjulian} 369871047Sjulian 3699172806Smavint 3700173605Sglebiusng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2, 3701172806Smav int flags) 3702172806Smav{ 3703172806Smav item_p item; 3704172806Smav 3705178259Smav if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) { 3706172806Smav return (ENOMEM); 3707172806Smav } 3708178259Smav item->el_flags |= NGQF_WRITER; 3709172806Smav NG_NODE_REF(node); /* and one for the item */ 3710172806Smav NGI_SET_NODE(item, node); 3711172806Smav if (hook) { 3712172806Smav NG_HOOK_REF(hook); 3713172806Smav NGI_SET_HOOK(item, hook); 3714172806Smav } 3715173605Sglebius NGI_FN(item) = fn; 3716172806Smav NGI_ARG1(item) = arg1; 3717172806Smav NGI_ARG2(item) = arg2; 3718172806Smav return(ng_snd_item(item, flags)); 3719172806Smav} 3720172806Smav 3721172806Smav/* 3722173605Sglebius * Send ng_item_fn2 function call to the specified node. 3723173605Sglebius * 3724173605Sglebius * If an optional pitem parameter is supplied, its apply 3725173605Sglebius * callback will be copied to the new item. If also NG_REUSE_ITEM 3726173605Sglebius * flag is set, no new item will be allocated, but pitem will 3727173605Sglebius * be used. 3728172806Smav */ 3729172806Smavint 3730173605Sglebiusng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1, 3731173605Sglebius int arg2, int flags) 3732172806Smav{ 3733172806Smav item_p item; 3734172806Smav 3735173605Sglebius KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0), 3736173605Sglebius ("%s: NG_REUSE_ITEM but no pitem", __func__)); 3737172806Smav 3738173605Sglebius /* 3739173605Sglebius * Allocate a new item if no supplied or 3740173605Sglebius * if we can't use supplied one. 3741173605Sglebius */ 3742173605Sglebius if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) { 3743178259Smav if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL) 3744173605Sglebius return (ENOMEM); 3745178259Smav if (pitem != NULL) 3746178259Smav item->apply = pitem->apply; 3747176849Smav } else { 3748178259Smav if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL) 3749178259Smav return (ENOMEM); 3750176849Smav } 3751172806Smav 3752178259Smav item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER; 3753172806Smav NG_NODE_REF(node); /* and one for the item */ 3754172806Smav NGI_SET_NODE(item, node); 3755172806Smav if (hook) { 3756172806Smav NG_HOOK_REF(hook); 3757172806Smav NGI_SET_HOOK(item, hook); 3758172806Smav } 3759172806Smav NGI_FN2(item) = fn; 3760172806Smav NGI_ARG1(item) = arg1; 3761172806Smav NGI_ARG2(item) = arg2; 3762172806Smav return(ng_snd_item(item, flags)); 3763172806Smav} 3764172806Smav 3765172806Smav/* 376691711Sjulian * Official timeout routines for Netgraph nodes. 376791711Sjulian */ 376891711Sjulianstatic void 3769140852Sglebiusng_callout_trampoline(void *arg) 377091711Sjulian{ 377191711Sjulian item_p item = arg; 377291711Sjulian 3773193731Szec CURVNET_SET(NGI_NODE(item)->nd_vnet); 377491711Sjulian ng_snd_item(item, 0); 3775193731Szec CURVNET_RESTORE(); 377691711Sjulian} 377791711Sjulian 3778137138Sglebiusint 3779138268Sglebiusng_callout(struct callout *c, node_p node, hook_p hook, int ticks, 378091711Sjulian ng_item_fn *fn, void * arg1, int arg2) 378191711Sjulian{ 3782149881Sglebius item_p item, oitem; 378391711Sjulian 3784178259Smav if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL) 3785137138Sglebius return (ENOMEM); 3786137138Sglebius 3787178259Smav item->el_flags |= NGQF_WRITER; 378891711Sjulian NG_NODE_REF(node); /* and one for the item */ 378991711Sjulian NGI_SET_NODE(item, node); 379091711Sjulian if (hook) { 379191711Sjulian NG_HOOK_REF(hook); 379291711Sjulian NGI_SET_HOOK(item, hook); 379391711Sjulian } 379491711Sjulian NGI_FN(item) = fn; 379591711Sjulian NGI_ARG1(item) = arg1; 379691711Sjulian NGI_ARG2(item) = arg2; 3797149881Sglebius oitem = c->c_arg; 3798149881Sglebius if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 && 3799149881Sglebius oitem != NULL) 3800149881Sglebius NG_FREE_ITEM(oitem); 3801137138Sglebius return (0); 380291711Sjulian} 380391711Sjulian 380491711Sjulian/* A special modified version of untimeout() */ 3805152451Sglebiusint 3806138268Sglebiusng_uncallout(struct callout *c, node_p node) 380791711Sjulian{ 380891711Sjulian item_p item; 3809137138Sglebius int rval; 3810149357Sglebius 3811149357Sglebius KASSERT(c != NULL, ("ng_uncallout: NULL callout")); 3812149357Sglebius KASSERT(node != NULL, ("ng_uncallout: NULL node")); 3813149357Sglebius 3814137230Sglebius rval = callout_stop(c); 3815137138Sglebius item = c->c_arg; 3816137138Sglebius /* Do an extra check */ 3817140852Sglebius if ((rval > 0) && (c->c_func == &ng_callout_trampoline) && 3818333615Ssbruno (item != NULL) && (NGI_NODE(item) == node)) { 381991711Sjulian /* 382091711Sjulian * We successfully removed it from the queue before it ran 3821152451Sglebius * So now we need to unreference everything that was 382291711Sjulian * given extra references. (NG_FREE_ITEM does this). 382391711Sjulian */ 382491711Sjulian NG_FREE_ITEM(item); 382591711Sjulian } 3826149881Sglebius c->c_arg = NULL; 3827137138Sglebius 3828310248Shselasky /* 3829310248Shselasky * Callers only want to know if the callout was cancelled and 3830310248Shselasky * not draining or stopped. 3831310248Shselasky */ 3832310248Shselasky return (rval > 0); 383391711Sjulian} 383491711Sjulian 383570700Sjulian/* 383670700Sjulian * Set the address, if none given, give the node here. 383770700Sjulian */ 383870700Sjulianvoid 383970700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr) 384070700Sjulian{ 384170700Sjulian if (retaddr) { 384270700Sjulian NGI_RETADDR(item) = retaddr; 384370700Sjulian } else { 384470700Sjulian /* 384570700Sjulian * The old return address should be ok. 384670700Sjulian * If there isn't one, use the address here. 384770700Sjulian */ 384870700Sjulian NGI_RETADDR(item) = ng_node2ID(here); 384970700Sjulian } 385070700Sjulian} 3851