ng_base.c revision 177953
152419Sjulian/* 252419Sjulian * ng_base.c 3139823Simp */ 4139823Simp 5139823Simp/*- 652419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc. 752419Sjulian * All rights reserved. 870700Sjulian * 952419Sjulian * Subject to the following obligations and disclaimer of warranty, use and 1052419Sjulian * redistribution of this software, in source or object code forms, with or 1152419Sjulian * without modifications are expressly permitted by Whistle Communications; 1252419Sjulian * provided, however, that: 1352419Sjulian * 1. Any and all reproductions of the source or object code must include the 1452419Sjulian * copyright notice above and the following disclaimer of warranties; and 1552419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle 1652419Sjulian * Communications, Inc. trademarks, including the mark "WHISTLE 1752419Sjulian * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1852419Sjulian * such appears in the above copyright notice or in the software. 1970700Sjulian * 2052419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 2152419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 2252419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 2352419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2452419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 2552419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2652419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 2752419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2852419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 2952419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 3052419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 3152419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 3252419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 3352419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3452419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3552419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3652419Sjulian * OF SUCH DAMAGE. 3752419Sjulian * 3867506Sjulian * Authors: Julian Elischer <julian@freebsd.org> 3967506Sjulian * Archie Cobbs <archie@freebsd.org> 4052419Sjulian * 4152419Sjulian * $FreeBSD: head/sys/netgraph/ng_base.c 177953 2008-04-06 15:26:32Z mav $ 4252419Sjulian * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $ 4352419Sjulian */ 4452419Sjulian 4552419Sjulian/* 4652419Sjulian * This file implements the base netgraph code. 4752419Sjulian */ 4852419Sjulian 4952419Sjulian#include <sys/param.h> 50139236Sglebius#include <sys/systm.h> 51139235Sglebius#include <sys/ctype.h> 5252419Sjulian#include <sys/errno.h> 53131933Smarcel#include <sys/kdb.h> 5452419Sjulian#include <sys/kernel.h> 55154225Sglebius#include <sys/ktr.h> 56114216Skan#include <sys/limits.h> 5752419Sjulian#include <sys/malloc.h> 58139235Sglebius#include <sys/mbuf.h> 5952419Sjulian#include <sys/queue.h> 6072946Sjulian#include <sys/sysctl.h> 61139235Sglebius#include <sys/syslog.h> 62172806Smav#include <sys/refcount.h> 63175847Smav#include <sys/proc.h> 64177953Smav#include <machine/cpu.h> 6552419Sjulian 6652419Sjulian#include <net/netisr.h> 6752419Sjulian 6852419Sjulian#include <netgraph/ng_message.h> 6952419Sjulian#include <netgraph/netgraph.h> 7053913Sarchie#include <netgraph/ng_parse.h> 7152419Sjulian 7272053SjulianMODULE_VERSION(netgraph, NG_ABI_VERSION); 7359756Speter 74151974Sglebius/* Mutex to protect topology events. */ 75151974Sglebiusstatic struct mtx ng_topo_mtx; 76151974Sglebius 7770784Sjulian#ifdef NETGRAPH_DEBUG 78176802Smavstatic struct mtx ng_nodelist_mtx; /* protects global node/hook lists */ 79152451Sglebiusstatic struct mtx ngq_mtx; /* protects the queue item list */ 8070784Sjulian 8170784Sjulianstatic SLIST_HEAD(, ng_node) ng_allnodes; 8270784Sjulianstatic LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */ 8370784Sjulianstatic SLIST_HEAD(, ng_hook) ng_allhooks; 8470784Sjulianstatic LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */ 8570784Sjulian 8670784Sjulianstatic void ng_dumpitems(void); 8770784Sjulianstatic void ng_dumpnodes(void); 8870784Sjulianstatic void ng_dumphooks(void); 8970784Sjulian 9070784Sjulian#endif /* NETGRAPH_DEBUG */ 9170935Sjulian/* 92152451Sglebius * DEAD versions of the structures. 9370935Sjulian * In order to avoid races, it is sometimes neccesary to point 94152451Sglebius * at SOMETHING even though theoretically, the current entity is 9570935Sjulian * INVALID. Use these to avoid these races. 9670935Sjulian */ 9770935Sjulianstruct ng_type ng_deadtype = { 9870935Sjulian NG_ABI_VERSION, 9970935Sjulian "dead", 10070935Sjulian NULL, /* modevent */ 10170935Sjulian NULL, /* constructor */ 10270935Sjulian NULL, /* rcvmsg */ 10370935Sjulian NULL, /* shutdown */ 10470935Sjulian NULL, /* newhook */ 10570935Sjulian NULL, /* findhook */ 10670935Sjulian NULL, /* connect */ 10770935Sjulian NULL, /* rcvdata */ 10870935Sjulian NULL, /* disconnect */ 10970935Sjulian NULL, /* cmdlist */ 11070935Sjulian}; 11170784Sjulian 11270935Sjulianstruct ng_node ng_deadnode = { 11370935Sjulian "dead", 11470935Sjulian &ng_deadtype, 115132464Sjulian NGF_INVALID, 11670935Sjulian 1, /* refs */ 11770935Sjulian 0, /* numhooks */ 11870935Sjulian NULL, /* private */ 11970935Sjulian 0, /* ID */ 12070935Sjulian LIST_HEAD_INITIALIZER(ng_deadnode.hooks), 12170935Sjulian {}, /* all_nodes list entry */ 12270935Sjulian {}, /* id hashtable list entry */ 12370935Sjulian {}, /* workqueue entry */ 12470935Sjulian { 0, 12570935Sjulian {}, /* should never use! (should hang) */ 12670935Sjulian NULL, 12770935Sjulian &ng_deadnode.nd_input_queue.queue, 12870935Sjulian &ng_deadnode 12970935Sjulian }, 13070935Sjulian#ifdef NETGRAPH_DEBUG 13170935Sjulian ND_MAGIC, 13270935Sjulian __FILE__, 13370935Sjulian __LINE__, 13470935Sjulian {NULL} 13570935Sjulian#endif /* NETGRAPH_DEBUG */ 13670935Sjulian}; 13770935Sjulian 13870935Sjulianstruct ng_hook ng_deadhook = { 13970935Sjulian "dead", 14070935Sjulian NULL, /* private */ 14170935Sjulian HK_INVALID | HK_DEAD, 14270935Sjulian 1, /* refs always >= 1 */ 143148261Sglebius 0, /* undefined data link type */ 14470935Sjulian &ng_deadhook, /* Peer is self */ 14570935Sjulian &ng_deadnode, /* attached to deadnode */ 14670935Sjulian {}, /* hooks list */ 14771885Sjulian NULL, /* override rcvmsg() */ 14871885Sjulian NULL, /* override rcvdata() */ 14970935Sjulian#ifdef NETGRAPH_DEBUG 15070935Sjulian HK_MAGIC, 15170935Sjulian __FILE__, 15270935Sjulian __LINE__, 15370935Sjulian {NULL} 15470935Sjulian#endif /* NETGRAPH_DEBUG */ 15570935Sjulian}; 15670935Sjulian 15770935Sjulian/* 15870935Sjulian * END DEAD STRUCTURES 15970935Sjulian */ 16070700Sjulian/* List nodes with unallocated work */ 16170700Sjulianstatic TAILQ_HEAD(, ng_node) ng_worklist = TAILQ_HEAD_INITIALIZER(ng_worklist); 16271902Sjulianstatic struct mtx ng_worklist_mtx; /* MUST LOCK NODE FIRST */ 16370700Sjulian 16452419Sjulian/* List of installed types */ 16570700Sjulianstatic LIST_HEAD(, ng_type) ng_typelist; 16670700Sjulianstatic struct mtx ng_typelist_mtx; 16752419Sjulian 16870700Sjulian/* Hash related definitions */ 16971354Sjulian/* XXX Don't need to initialise them because it's a LIST */ 170176802Smav#define NG_ID_HASH_SIZE 128 /* most systems wont need even this many */ 17171354Sjulianstatic LIST_HEAD(, ng_node) ng_ID_hash[NG_ID_HASH_SIZE]; 17270700Sjulianstatic struct mtx ng_idhash_mtx; 17371354Sjulian/* Method to find a node.. used twice so do it here */ 17471354Sjulian#define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE)) 17571354Sjulian#define NG_IDHASH_FIND(ID, node) \ 17671354Sjulian do { \ 177131008Srwatson mtx_assert(&ng_idhash_mtx, MA_OWNED); \ 17871354Sjulian LIST_FOREACH(node, &ng_ID_hash[NG_IDHASH_FN(ID)], \ 17971354Sjulian nd_idnodes) { \ 18071354Sjulian if (NG_NODE_IS_VALID(node) \ 18171354Sjulian && (NG_NODE_ID(node) == ID)) { \ 18271354Sjulian break; \ 18371354Sjulian } \ 18471354Sjulian } \ 18571354Sjulian } while (0) 18652722Sjulian 187176802Smav#define NG_NAME_HASH_SIZE 128 /* most systems wont need even this many */ 188176802Smavstatic LIST_HEAD(, ng_node) ng_name_hash[NG_NAME_HASH_SIZE]; 189176802Smavstatic struct mtx ng_namehash_mtx; 190176802Smav#define NG_NAMEHASH(NAME, HASH) \ 191176802Smav do { \ 192176802Smav u_char h = 0; \ 193176802Smav const u_char *c; \ 194176802Smav for (c = (const u_char*)(NAME); *c; c++)\ 195176802Smav h += *c; \ 196176802Smav (HASH) = h % (NG_NAME_HASH_SIZE); \ 197176802Smav } while (0) 19870700Sjulian 199176802Smav 20052419Sjulian/* Internal functions */ 20152419Sjulianstatic int ng_add_hook(node_p node, const char *name, hook_p * hookp); 20270700Sjulianstatic int ng_generic_msg(node_p here, item_p item, hook_p lasthook); 20352722Sjulianstatic ng_ID_t ng_decodeidname(const char *name); 20452419Sjulianstatic int ngb_mod_event(module_t mod, int event, void *data); 205177953Smavstatic void ng_worklist_add(node_p node); 20652419Sjulianstatic void ngintr(void); 207170180Sglebiusstatic int ng_apply_item(node_p node, item_p item, int rw); 20870700Sjulianstatic void ng_flush_input_queue(struct ng_queue * ngq); 20970700Sjulianstatic node_p ng_ID2noderef(ng_ID_t ID); 210172806Smavstatic int ng_con_nodes(item_p item, node_p node, const char *name, 211172806Smav node_p node2, const char *name2); 212172806Smavstatic int ng_con_part2(node_p node, item_p item, hook_p hook); 213172806Smavstatic int ng_con_part3(node_p node, item_p item, hook_p hook); 21471047Sjulianstatic int ng_mkpeer(node_p node, const char *name, 21571047Sjulian const char *name2, char *type); 21652419Sjulian 217152451Sglebius/* Imported, these used to be externally visible, some may go back. */ 21870700Sjulianvoid ng_destroy_hook(hook_p hook); 21970700Sjuliannode_p ng_name2noderef(node_p node, const char *name); 22070700Sjulianint ng_path2noderef(node_p here, const char *path, 22170700Sjulian node_p *dest, hook_p *lasthook); 22270700Sjulianint ng_make_node(const char *type, node_p *nodepp); 22370700Sjulianint ng_path_parse(char *addr, char **node, char **path, char **hook); 22471849Sjulianvoid ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3); 22570784Sjulianvoid ng_unname(node_p node); 22670700Sjulian 22770700Sjulian 22852419Sjulian/* Our own netgraph malloc type */ 22952419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); 23070700SjulianMALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures"); 23170700SjulianMALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures"); 23270700SjulianMALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures"); 23370700SjulianMALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage"); 23452419Sjulian 23570700Sjulian/* Should not be visible outside this file */ 23670784Sjulian 23770784Sjulian#define _NG_ALLOC_HOOK(hook) \ 23870784Sjulian MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO) 23970784Sjulian#define _NG_ALLOC_NODE(node) \ 24070784Sjulian MALLOC(node, node_p, sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO) 24170784Sjulian 242168049Swkoszek#define NG_QUEUE_LOCK_INIT(n) \ 243168137Swkoszek mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF) 244168049Swkoszek#define NG_QUEUE_LOCK(n) \ 245168137Swkoszek mtx_lock(&(n)->q_mtx) 246168049Swkoszek#define NG_QUEUE_UNLOCK(n) \ 247168137Swkoszek mtx_unlock(&(n)->q_mtx) 248168049Swkoszek#define NG_WORKLIST_LOCK_INIT() \ 249168137Swkoszek mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF) 250168049Swkoszek#define NG_WORKLIST_LOCK() \ 251168137Swkoszek mtx_lock(&ng_worklist_mtx) 252168049Swkoszek#define NG_WORKLIST_UNLOCK() \ 253168137Swkoszek mtx_unlock(&ng_worklist_mtx) 254168049Swkoszek 25570784Sjulian#ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ 25670784Sjulian/* 25770784Sjulian * In debug mode: 25870784Sjulian * In an attempt to help track reference count screwups 25970784Sjulian * we do not free objects back to the malloc system, but keep them 26070784Sjulian * in a local cache where we can examine them and keep information safely 26170784Sjulian * after they have been freed. 26270784Sjulian * We use this scheme for nodes and hooks, and to some extent for items. 26370784Sjulian */ 26470784Sjulianstatic __inline hook_p 26570784Sjulianng_alloc_hook(void) 26670784Sjulian{ 26770784Sjulian hook_p hook; 26870784Sjulian SLIST_ENTRY(ng_hook) temp; 26972200Sbmilekic mtx_lock(&ng_nodelist_mtx); 27070784Sjulian hook = LIST_FIRST(&ng_freehooks); 27170784Sjulian if (hook) { 27270784Sjulian LIST_REMOVE(hook, hk_hooks); 27370784Sjulian bcopy(&hook->hk_all, &temp, sizeof(temp)); 27470784Sjulian bzero(hook, sizeof(struct ng_hook)); 27570784Sjulian bcopy(&temp, &hook->hk_all, sizeof(temp)); 27672200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 27770784Sjulian hook->hk_magic = HK_MAGIC; 27870784Sjulian } else { 27972200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 28070784Sjulian _NG_ALLOC_HOOK(hook); 28170784Sjulian if (hook) { 28270784Sjulian hook->hk_magic = HK_MAGIC; 28372200Sbmilekic mtx_lock(&ng_nodelist_mtx); 28470784Sjulian SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all); 28572200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 28670784Sjulian } 28770784Sjulian } 28870784Sjulian return (hook); 28970784Sjulian} 29070784Sjulian 29170784Sjulianstatic __inline node_p 29270784Sjulianng_alloc_node(void) 29370784Sjulian{ 29470784Sjulian node_p node; 29570784Sjulian SLIST_ENTRY(ng_node) temp; 29672200Sbmilekic mtx_lock(&ng_nodelist_mtx); 29770784Sjulian node = LIST_FIRST(&ng_freenodes); 29870784Sjulian if (node) { 29970784Sjulian LIST_REMOVE(node, nd_nodes); 30070784Sjulian bcopy(&node->nd_all, &temp, sizeof(temp)); 30170784Sjulian bzero(node, sizeof(struct ng_node)); 30270784Sjulian bcopy(&temp, &node->nd_all, sizeof(temp)); 30372200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 30470784Sjulian node->nd_magic = ND_MAGIC; 30570784Sjulian } else { 30672200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 30770784Sjulian _NG_ALLOC_NODE(node); 30870784Sjulian if (node) { 30970784Sjulian node->nd_magic = ND_MAGIC; 31072200Sbmilekic mtx_lock(&ng_nodelist_mtx); 31170784Sjulian SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all); 31272200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 31370784Sjulian } 31470784Sjulian } 31570784Sjulian return (node); 31670784Sjulian} 31770784Sjulian 31870784Sjulian#define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0) 31970784Sjulian#define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0) 32070784Sjulian 32170784Sjulian 32270784Sjulian#define NG_FREE_HOOK(hook) \ 32370784Sjulian do { \ 32472200Sbmilekic mtx_lock(&ng_nodelist_mtx); \ 32570784Sjulian LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks); \ 32670784Sjulian hook->hk_magic = 0; \ 32772200Sbmilekic mtx_unlock(&ng_nodelist_mtx); \ 32870784Sjulian } while (0) 32970784Sjulian 33070784Sjulian#define NG_FREE_NODE(node) \ 33170784Sjulian do { \ 33272200Sbmilekic mtx_lock(&ng_nodelist_mtx); \ 33370784Sjulian LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes); \ 33470784Sjulian node->nd_magic = 0; \ 33572200Sbmilekic mtx_unlock(&ng_nodelist_mtx); \ 33670784Sjulian } while (0) 33770784Sjulian 33870784Sjulian#else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ 33970784Sjulian 34070784Sjulian#define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook) 34170784Sjulian#define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node) 34270784Sjulian 34370700Sjulian#define NG_FREE_HOOK(hook) do { FREE((hook), M_NETGRAPH_HOOK); } while (0) 34470700Sjulian#define NG_FREE_NODE(node) do { FREE((node), M_NETGRAPH_NODE); } while (0) 34570784Sjulian 34670784Sjulian#endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ 34770784Sjulian 348131933Smarcel/* Set this to kdb_enter("X") to catch all errors as they occur */ 34952419Sjulian#ifndef TRAP_ERROR 35071047Sjulian#define TRAP_ERROR() 35152419Sjulian#endif 35252419Sjulian 35352722Sjulianstatic ng_ID_t nextID = 1; 35452722Sjulian 35553403Sarchie#ifdef INVARIANTS 35653403Sarchie#define CHECK_DATA_MBUF(m) do { \ 35753403Sarchie struct mbuf *n; \ 35853403Sarchie int total; \ 35953403Sarchie \ 360113255Sdes M_ASSERTPKTHDR(m); \ 361149818Sglebius for (total = 0, n = (m); n != NULL; n = n->m_next) { \ 36253403Sarchie total += n->m_len; \ 363149818Sglebius if (n->m_nextpkt != NULL) \ 364149818Sglebius panic("%s: m_nextpkt", __func__); \ 365149818Sglebius } \ 366149827Sglebius \ 36753403Sarchie if ((m)->m_pkthdr.len != total) { \ 36853403Sarchie panic("%s: %d != %d", \ 36987599Sobrien __func__, (m)->m_pkthdr.len, total); \ 37053403Sarchie } \ 37153403Sarchie } while (0) 37253403Sarchie#else 37353403Sarchie#define CHECK_DATA_MBUF(m) 37453403Sarchie#endif 37552722Sjulian 376172806Smav#define ERROUT(x) do { error = (x); goto done; } while (0) 37753403Sarchie 37852419Sjulian/************************************************************************ 37953913Sarchie Parse type definitions for generic messages 38053913Sarchie************************************************************************/ 38153913Sarchie 38253913Sarchie/* Handy structure parse type defining macro */ 38353913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ 38497685Sarchiestatic const struct ng_parse_struct_field \ 38597685Sarchie ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args; \ 38653913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = { \ 38753913Sarchie &ng_parse_struct_type, \ 38897685Sarchie &ng_ ## lo ## _type_fields \ 38953913Sarchie} 39053913Sarchie 39153913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); 39253913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); 39353913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); 39453913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); 39553913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); 39653913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); 39753913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); 39853913Sarchie 39953913Sarchie/* Get length of an array when the length is stored as a 32 bit 40072645Sasmodai value immediately preceding the array -- as with struct namelist 40153913Sarchie and struct typelist. */ 40253913Sarchiestatic int 40353913Sarchieng_generic_list_getLength(const struct ng_parse_type *type, 40453913Sarchie const u_char *start, const u_char *buf) 40553913Sarchie{ 40653913Sarchie return *((const u_int32_t *)(buf - 4)); 40753913Sarchie} 40853913Sarchie 40953913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */ 41053913Sarchiestatic int 41153913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type, 41253913Sarchie const u_char *start, const u_char *buf) 41353913Sarchie{ 41453913Sarchie const struct hooklist *hl = (const struct hooklist *)start; 41553913Sarchie 41653913Sarchie return hl->nodeinfo.hooks; 41753913Sarchie} 41853913Sarchie 41953913Sarchie/* Array type for a variable length array of struct namelist */ 42053913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = { 42153913Sarchie &ng_generic_nodeinfo_type, 42253913Sarchie &ng_generic_list_getLength 42353913Sarchie}; 42453913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = { 42553913Sarchie &ng_parse_array_type, 42653913Sarchie &ng_nodeinfoarray_type_info 42753913Sarchie}; 42853913Sarchie 42953913Sarchie/* Array type for a variable length array of struct typelist */ 43053913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = { 43153913Sarchie &ng_generic_typeinfo_type, 43253913Sarchie &ng_generic_list_getLength 43353913Sarchie}; 43453913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = { 43553913Sarchie &ng_parse_array_type, 43653913Sarchie &ng_typeinfoarray_type_info 43753913Sarchie}; 43853913Sarchie 43953913Sarchie/* Array type for array of struct linkinfo in struct hooklist */ 44053913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { 44153913Sarchie &ng_generic_linkinfo_type, 44253913Sarchie &ng_generic_linkinfo_getLength 44353913Sarchie}; 44453913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = { 44553913Sarchie &ng_parse_array_type, 44653913Sarchie &ng_generic_linkinfo_array_type_info 44753913Sarchie}; 44853913Sarchie 44953913SarchieDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); 45053913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, 45153913Sarchie (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); 45253913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, 45353913Sarchie (&ng_generic_nodeinfoarray_type)); 45453913Sarchie 45553913Sarchie/* List of commands and how to convert arguments to/from ASCII */ 45653913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = { 45753913Sarchie { 45853913Sarchie NGM_GENERIC_COOKIE, 45953913Sarchie NGM_SHUTDOWN, 46053913Sarchie "shutdown", 46153913Sarchie NULL, 46253913Sarchie NULL 46353913Sarchie }, 46453913Sarchie { 46553913Sarchie NGM_GENERIC_COOKIE, 46653913Sarchie NGM_MKPEER, 46753913Sarchie "mkpeer", 46853913Sarchie &ng_generic_mkpeer_type, 46953913Sarchie NULL 47053913Sarchie }, 47153913Sarchie { 47253913Sarchie NGM_GENERIC_COOKIE, 47353913Sarchie NGM_CONNECT, 47453913Sarchie "connect", 47553913Sarchie &ng_generic_connect_type, 47653913Sarchie NULL 47753913Sarchie }, 47853913Sarchie { 47953913Sarchie NGM_GENERIC_COOKIE, 48053913Sarchie NGM_NAME, 48153913Sarchie "name", 48253913Sarchie &ng_generic_name_type, 48353913Sarchie NULL 48453913Sarchie }, 48553913Sarchie { 48653913Sarchie NGM_GENERIC_COOKIE, 48753913Sarchie NGM_RMHOOK, 48853913Sarchie "rmhook", 48953913Sarchie &ng_generic_rmhook_type, 49053913Sarchie NULL 49153913Sarchie }, 49253913Sarchie { 49353913Sarchie NGM_GENERIC_COOKIE, 49453913Sarchie NGM_NODEINFO, 49553913Sarchie "nodeinfo", 49653913Sarchie NULL, 49753913Sarchie &ng_generic_nodeinfo_type 49853913Sarchie }, 49953913Sarchie { 50053913Sarchie NGM_GENERIC_COOKIE, 50153913Sarchie NGM_LISTHOOKS, 50253913Sarchie "listhooks", 50353913Sarchie NULL, 50453913Sarchie &ng_generic_hooklist_type 50553913Sarchie }, 50653913Sarchie { 50753913Sarchie NGM_GENERIC_COOKIE, 50853913Sarchie NGM_LISTNAMES, 50953913Sarchie "listnames", 51053913Sarchie NULL, 51153913Sarchie &ng_generic_listnodes_type /* same as NGM_LISTNODES */ 51253913Sarchie }, 51353913Sarchie { 51453913Sarchie NGM_GENERIC_COOKIE, 51553913Sarchie NGM_LISTNODES, 51653913Sarchie "listnodes", 51753913Sarchie NULL, 51853913Sarchie &ng_generic_listnodes_type 51953913Sarchie }, 52053913Sarchie { 52153913Sarchie NGM_GENERIC_COOKIE, 52253913Sarchie NGM_LISTTYPES, 52353913Sarchie "listtypes", 52453913Sarchie NULL, 52553913Sarchie &ng_generic_typeinfo_type 52653913Sarchie }, 52753913Sarchie { 52853913Sarchie NGM_GENERIC_COOKIE, 52962471Sphk NGM_TEXT_CONFIG, 53062471Sphk "textconfig", 53162471Sphk NULL, 53262471Sphk &ng_parse_string_type 53362471Sphk }, 53462471Sphk { 53562471Sphk NGM_GENERIC_COOKIE, 53653913Sarchie NGM_TEXT_STATUS, 53753913Sarchie "textstatus", 53853913Sarchie NULL, 53953913Sarchie &ng_parse_string_type 54053913Sarchie }, 54153913Sarchie { 54253913Sarchie NGM_GENERIC_COOKIE, 54353913Sarchie NGM_ASCII2BINARY, 54453913Sarchie "ascii2binary", 54553913Sarchie &ng_parse_ng_mesg_type, 54653913Sarchie &ng_parse_ng_mesg_type 54753913Sarchie }, 54853913Sarchie { 54953913Sarchie NGM_GENERIC_COOKIE, 55053913Sarchie NGM_BINARY2ASCII, 55153913Sarchie "binary2ascii", 55253913Sarchie &ng_parse_ng_mesg_type, 55353913Sarchie &ng_parse_ng_mesg_type 55453913Sarchie }, 55553913Sarchie { 0 } 55653913Sarchie}; 55753913Sarchie 55853913Sarchie/************************************************************************ 55952419Sjulian Node routines 56052419Sjulian************************************************************************/ 56152419Sjulian 56252419Sjulian/* 56352419Sjulian * Instantiate a node of the requested type 56452419Sjulian */ 56552419Sjulianint 56652419Sjulianng_make_node(const char *typename, node_p *nodepp) 56752419Sjulian{ 56852419Sjulian struct ng_type *type; 56970700Sjulian int error; 57052419Sjulian 57152419Sjulian /* Check that the type makes sense */ 57252419Sjulian if (typename == NULL) { 57371047Sjulian TRAP_ERROR(); 57452419Sjulian return (EINVAL); 57552419Sjulian } 57652419Sjulian 577132705Sglebius /* Locate the node type. If we fail we return. Do not try to load 578132705Sglebius * module. 579132705Sglebius */ 580132705Sglebius if ((type = ng_findtype(typename)) == NULL) 581132705Sglebius return (ENXIO); 58252419Sjulian 58370700Sjulian /* 58470700Sjulian * If we have a constructor, then make the node and 58570700Sjulian * call the constructor to do type specific initialisation. 58670700Sjulian */ 58770700Sjulian if (type->constructor != NULL) { 58870700Sjulian if ((error = ng_make_node_common(type, nodepp)) == 0) { 58970700Sjulian if ((error = ((*type->constructor)(*nodepp)) != 0)) { 59070784Sjulian NG_NODE_UNREF(*nodepp); 59170700Sjulian } 59270700Sjulian } 59370700Sjulian } else { 59470700Sjulian /* 59570700Sjulian * Node has no constructor. We cannot ask for one 596167677Srwatson * to be made. It must be brought into existence by 59770935Sjulian * some external agency. The external agency should 59870700Sjulian * call ng_make_node_common() directly to get the 59970700Sjulian * netgraph part initialised. 60070700Sjulian */ 60171047Sjulian TRAP_ERROR(); 60270700Sjulian error = EINVAL; 60370700Sjulian } 60470700Sjulian return (error); 60552419Sjulian} 60652419Sjulian 60752419Sjulian/* 60870700Sjulian * Generic node creation. Called by node initialisation for externally 60970700Sjulian * instantiated nodes (e.g. hardware, sockets, etc ). 61052419Sjulian * The returned node has a reference count of 1. 61152419Sjulian */ 61252419Sjulianint 61352419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp) 61452419Sjulian{ 61552419Sjulian node_p node; 61652419Sjulian 61752419Sjulian /* Require the node type to have been already installed */ 61852419Sjulian if (ng_findtype(type->name) == NULL) { 61971047Sjulian TRAP_ERROR(); 62052419Sjulian return (EINVAL); 62152419Sjulian } 62252419Sjulian 62352419Sjulian /* Make a node and try attach it to the type */ 62470784Sjulian NG_ALLOC_NODE(node); 62552419Sjulian if (node == NULL) { 62671047Sjulian TRAP_ERROR(); 62752419Sjulian return (ENOMEM); 62852419Sjulian } 62970784Sjulian node->nd_type = type; 63070784Sjulian NG_NODE_REF(node); /* note reference */ 63152419Sjulian type->refs++; 63252419Sjulian 633168049Swkoszek NG_QUEUE_LOCK_INIT(&node->nd_input_queue); 63470784Sjulian node->nd_input_queue.queue = NULL; 63570784Sjulian node->nd_input_queue.last = &node->nd_input_queue.queue; 63670784Sjulian node->nd_input_queue.q_flags = 0; 63770784Sjulian node->nd_input_queue.q_node = node; 63852419Sjulian 63952419Sjulian /* Initialize hook list for new node */ 64070784Sjulian LIST_INIT(&node->nd_hooks); 64152419Sjulian 642176802Smav /* Link us into the name hash. */ 643176802Smav mtx_lock(&ng_namehash_mtx); 644176802Smav LIST_INSERT_HEAD(&ng_name_hash[0], node, nd_nodes); 645176802Smav mtx_unlock(&ng_namehash_mtx); 64670700Sjulian 64752722Sjulian /* get an ID and put us in the hash chain */ 64872200Sbmilekic mtx_lock(&ng_idhash_mtx); 64970784Sjulian for (;;) { /* wrap protection, even if silly */ 65070700Sjulian node_p node2 = NULL; 65170784Sjulian node->nd_ID = nextID++; /* 137/second for 1 year before wrap */ 65271354Sjulian 65370784Sjulian /* Is there a problem with the new number? */ 65471354Sjulian NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */ 65571354Sjulian if ((node->nd_ID != 0) && (node2 == NULL)) { 65670784Sjulian break; 65770700Sjulian } 65870784Sjulian } 65971354Sjulian LIST_INSERT_HEAD(&ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], 66070784Sjulian node, nd_idnodes); 66172200Sbmilekic mtx_unlock(&ng_idhash_mtx); 66252722Sjulian 66352419Sjulian /* Done */ 66452419Sjulian *nodepp = node; 66552419Sjulian return (0); 66652419Sjulian} 66752419Sjulian 66852419Sjulian/* 66952419Sjulian * Forceably start the shutdown process on a node. Either call 670167677Srwatson * its shutdown method, or do the default shutdown if there is 67152419Sjulian * no type-specific method. 67252419Sjulian * 673167677Srwatson * We can only be called from a shutdown message, so we know we have 67470939Sjulian * a writer lock, and therefore exclusive access. It also means 67570939Sjulian * that we should not be on the work queue, but we check anyhow. 67670700Sjulian * 67770700Sjulian * Persistent node types must have a type-specific method which 678167677Srwatson * allocates a new node in which case, this one is irretrievably going away, 67970939Sjulian * or cleans up anything it needs, and just makes the node valid again, 680152451Sglebius * in which case we allow the node to survive. 68170939Sjulian * 682167677Srwatson * XXX We need to think of how to tell a persistent node that we 68370939Sjulian * REALLY need to go away because the hardware has gone or we 68470939Sjulian * are rebooting.... etc. 68552419Sjulian */ 68652419Sjulianvoid 68771849Sjulianng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3) 68852419Sjulian{ 68970939Sjulian hook_p hook; 69070939Sjulian 69152419Sjulian /* Check if it's already shutting down */ 692132464Sjulian if ((node->nd_flags & NGF_CLOSING) != 0) 69352419Sjulian return; 69452419Sjulian 69571849Sjulian if (node == &ng_deadnode) { 69671849Sjulian printf ("shutdown called on deadnode\n"); 69771849Sjulian return; 69871849Sjulian } 69971849Sjulian 70052419Sjulian /* Add an extra reference so it doesn't go away during this */ 70170784Sjulian NG_NODE_REF(node); 70252419Sjulian 70370784Sjulian /* 70470784Sjulian * Mark it invalid so any newcomers know not to try use it 70570784Sjulian * Also add our own mark so we can't recurse 706132464Sjulian * note that NGF_INVALID does not do this as it's also set during 70770784Sjulian * creation 70870784Sjulian */ 709132464Sjulian node->nd_flags |= NGF_INVALID|NGF_CLOSING; 71052419Sjulian 711129836Sjulian /* If node has its pre-shutdown method, then call it first*/ 712129836Sjulian if (node->nd_type && node->nd_type->close) 713129836Sjulian (*node->nd_type->close)(node); 714129836Sjulian 71570939Sjulian /* Notify all remaining connected nodes to disconnect */ 71670939Sjulian while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL) 71770939Sjulian ng_destroy_hook(hook); 71870784Sjulian 71970700Sjulian /* 72070700Sjulian * Drain the input queue forceably. 72170784Sjulian * it has no hooks so what's it going to do, bleed on someone? 72270784Sjulian * Theoretically we came here from a queue entry that was added 72370784Sjulian * Just before the queue was closed, so it should be empty anyway. 72471902Sjulian * Also removes us from worklist if needed. 72570700Sjulian */ 72670784Sjulian ng_flush_input_queue(&node->nd_input_queue); 72770700Sjulian 72852419Sjulian /* Ask the type if it has anything to do in this case */ 72970784Sjulian if (node->nd_type && node->nd_type->shutdown) { 73070784Sjulian (*node->nd_type->shutdown)(node); 73171849Sjulian if (NG_NODE_IS_VALID(node)) { 73271849Sjulian /* 73371849Sjulian * Well, blow me down if the node code hasn't declared 73471849Sjulian * that it doesn't want to die. 73571849Sjulian * Presumably it is a persistant node. 73671849Sjulian * If we REALLY want it to go away, 73771849Sjulian * e.g. hardware going away, 738132464Sjulian * Our caller should set NGF_REALLY_DIE in nd_flags. 739152451Sglebius */ 740132464Sjulian node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING); 74171849Sjulian NG_NODE_UNREF(node); /* Assume they still have theirs */ 74271849Sjulian return; 74371849Sjulian } 74470700Sjulian } else { /* do the default thing */ 74570784Sjulian NG_NODE_UNREF(node); 74652419Sjulian } 74752419Sjulian 74870784Sjulian ng_unname(node); /* basically a NOP these days */ 74970784Sjulian 75070784Sjulian /* 75170784Sjulian * Remove extra reference, possibly the last 75270784Sjulian * Possible other holders of references may include 75370784Sjulian * timeout callouts, but theoretically the node's supposed to 75470784Sjulian * have cancelled them. Possibly hardware dependencies may 75570784Sjulian * force a driver to 'linger' with a reference. 75670784Sjulian */ 75770784Sjulian NG_NODE_UNREF(node); 75852419Sjulian} 75952419Sjulian 76052419Sjulian/* 76174078Sjulian * Remove a reference to the node, possibly the last. 76274078Sjulian * deadnode always acts as it it were the last. 76352419Sjulian */ 76474078Sjulianint 76570784Sjulianng_unref_node(node_p node) 76652419Sjulian{ 767152451Sglebius int v; 76871047Sjulian 76971047Sjulian if (node == &ng_deadnode) { 77074078Sjulian return (0); 77171047Sjulian } 77271047Sjulian 773177728Smav v = atomic_fetchadd_int(&node->nd_refs, -1); 77469519Sjulian 775177728Smav if (v == 1) { /* we were the last */ 77670700Sjulian 777176802Smav mtx_lock(&ng_namehash_mtx); 77870784Sjulian node->nd_type->refs--; /* XXX maybe should get types lock? */ 77970784Sjulian LIST_REMOVE(node, nd_nodes); 780176802Smav mtx_unlock(&ng_namehash_mtx); 78170700Sjulian 78272200Sbmilekic mtx_lock(&ng_idhash_mtx); 78370784Sjulian LIST_REMOVE(node, nd_idnodes); 78472200Sbmilekic mtx_unlock(&ng_idhash_mtx); 78552419Sjulian 78670791Sjulian mtx_destroy(&node->nd_input_queue.q_mtx); 78770700Sjulian NG_FREE_NODE(node); 78852419Sjulian } 789177728Smav return (v - 1); 79052419Sjulian} 79152419Sjulian 79252419Sjulian/************************************************************************ 79352722Sjulian Node ID handling 79452722Sjulian************************************************************************/ 79552722Sjulianstatic node_p 79670700Sjulianng_ID2noderef(ng_ID_t ID) 79752722Sjulian{ 79870784Sjulian node_p node; 79972200Sbmilekic mtx_lock(&ng_idhash_mtx); 80071354Sjulian NG_IDHASH_FIND(ID, node); 80170784Sjulian if(node) 80270784Sjulian NG_NODE_REF(node); 80372200Sbmilekic mtx_unlock(&ng_idhash_mtx); 80470784Sjulian return(node); 80552722Sjulian} 80652722Sjulian 80752722Sjulianng_ID_t 80852722Sjulianng_node2ID(node_p node) 80952722Sjulian{ 81070912Sjulian return (node ? NG_NODE_ID(node) : 0); 81152722Sjulian} 81252722Sjulian 81352722Sjulian/************************************************************************ 81452419Sjulian Node name handling 81552419Sjulian************************************************************************/ 81652419Sjulian 81752419Sjulian/* 81852419Sjulian * Assign a node a name. Once assigned, the name cannot be changed. 81952419Sjulian */ 82052419Sjulianint 82152419Sjulianng_name_node(node_p node, const char *name) 82252419Sjulian{ 823176802Smav int i, hash; 82470700Sjulian node_p node2; 82552419Sjulian 82652419Sjulian /* Check the name is valid */ 827125028Sharti for (i = 0; i < NG_NODESIZ; i++) { 82852419Sjulian if (name[i] == '\0' || name[i] == '.' || name[i] == ':') 82952419Sjulian break; 83052419Sjulian } 83152419Sjulian if (i == 0 || name[i] != '\0') { 83271047Sjulian TRAP_ERROR(); 83352419Sjulian return (EINVAL); 83452419Sjulian } 83552722Sjulian if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ 83671047Sjulian TRAP_ERROR(); 83752419Sjulian return (EINVAL); 83852419Sjulian } 83952419Sjulian 84052419Sjulian /* Check the name isn't already being used */ 84170700Sjulian if ((node2 = ng_name2noderef(node, name)) != NULL) { 84270784Sjulian NG_NODE_UNREF(node2); 84371047Sjulian TRAP_ERROR(); 84452419Sjulian return (EADDRINUSE); 84552419Sjulian } 84652419Sjulian 84770700Sjulian /* copy it */ 848125028Sharti strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ); 84952419Sjulian 850176802Smav /* Update name hash. */ 851176802Smav NG_NAMEHASH(name, hash); 852176802Smav mtx_lock(&ng_namehash_mtx); 853176802Smav LIST_REMOVE(node, nd_nodes); 854176802Smav LIST_INSERT_HEAD(&ng_name_hash[hash], node, nd_nodes); 855176802Smav mtx_unlock(&ng_namehash_mtx); 856176802Smav 85752419Sjulian return (0); 85852419Sjulian} 85952419Sjulian 86052419Sjulian/* 86152419Sjulian * Find a node by absolute name. The name should NOT end with ':' 86252419Sjulian * The name "." means "this node" and "[xxx]" means "the node 86352419Sjulian * with ID (ie, at address) xxx". 86452419Sjulian * 86552419Sjulian * Returns the node if found, else NULL. 86670700Sjulian * Eventually should add something faster than a sequential search. 867170035Srwatson * Note it acquires a reference on the node so you can be sure it's still 868170035Srwatson * there. 86952419Sjulian */ 87052419Sjuliannode_p 87170700Sjulianng_name2noderef(node_p here, const char *name) 87252419Sjulian{ 87352722Sjulian node_p node; 87452722Sjulian ng_ID_t temp; 875176802Smav int hash; 87652419Sjulian 87752419Sjulian /* "." means "this node" */ 87870700Sjulian if (strcmp(name, ".") == 0) { 87970784Sjulian NG_NODE_REF(here); 88070700Sjulian return(here); 88170700Sjulian } 88252419Sjulian 88352419Sjulian /* Check for name-by-ID */ 88452722Sjulian if ((temp = ng_decodeidname(name)) != 0) { 88570700Sjulian return (ng_ID2noderef(temp)); 88652419Sjulian } 88752419Sjulian 88852419Sjulian /* Find node by name */ 889176802Smav NG_NAMEHASH(name, hash); 890176802Smav mtx_lock(&ng_namehash_mtx); 891176802Smav LIST_FOREACH(node, &ng_name_hash[hash], nd_nodes) { 892176802Smav if (NG_NODE_IS_VALID(node) && 893176802Smav (strcmp(NG_NODE_NAME(node), name) == 0)) { 89452419Sjulian break; 89570912Sjulian } 89652419Sjulian } 89770700Sjulian if (node) 89870784Sjulian NG_NODE_REF(node); 899176802Smav mtx_unlock(&ng_namehash_mtx); 90052419Sjulian return (node); 90152419Sjulian} 90252419Sjulian 90352419Sjulian/* 904108533Sschweikh * Decode an ID name, eg. "[f03034de]". Returns 0 if the 90552722Sjulian * string is not valid, otherwise returns the value. 90652419Sjulian */ 90752722Sjulianstatic ng_ID_t 90852419Sjulianng_decodeidname(const char *name) 90952419Sjulian{ 91052816Sarchie const int len = strlen(name); 91153648Sarchie char *eptr; 91252816Sarchie u_long val; 91352419Sjulian 91452816Sarchie /* Check for proper length, brackets, no leading junk */ 91570912Sjulian if ((len < 3) 91670912Sjulian || (name[0] != '[') 91770912Sjulian || (name[len - 1] != ']') 91870912Sjulian || (!isxdigit(name[1]))) { 91970912Sjulian return ((ng_ID_t)0); 92070912Sjulian } 92152419Sjulian 92252816Sarchie /* Decode number */ 92352816Sarchie val = strtoul(name + 1, &eptr, 16); 92470912Sjulian if ((eptr - name != len - 1) 92570912Sjulian || (val == ULONG_MAX) 92670912Sjulian || (val == 0)) { 92753042Sjulian return ((ng_ID_t)0); 92870912Sjulian } 92952816Sarchie return (ng_ID_t)val; 93052419Sjulian} 93152419Sjulian 93252419Sjulian/* 93352419Sjulian * Remove a name from a node. This should only be called 93452419Sjulian * when shutting down and removing the node. 935167677Srwatson * IF we allow name changing this may be more resurrected. 93652419Sjulian */ 93752419Sjulianvoid 93852419Sjulianng_unname(node_p node) 93952419Sjulian{ 94052419Sjulian} 94152419Sjulian 94252419Sjulian/************************************************************************ 94352419Sjulian Hook routines 94452419Sjulian Names are not optional. Hooks are always connected, except for a 94570939Sjulian brief moment within these routines. On invalidation or during creation 94670939Sjulian they are connected to the 'dead' hook. 94752419Sjulian************************************************************************/ 94852419Sjulian 94952419Sjulian/* 95052419Sjulian * Remove a hook reference 95152419Sjulian */ 95270784Sjulianvoid 95352419Sjulianng_unref_hook(hook_p hook) 95452419Sjulian{ 955152451Sglebius int v; 95671047Sjulian 95771047Sjulian if (hook == &ng_deadhook) { 95871047Sjulian return; 95971047Sjulian } 96069519Sjulian 961177728Smav v = atomic_fetchadd_int(&hook->hk_refs, -1); 962177728Smav 96370784Sjulian if (v == 1) { /* we were the last */ 964177722Smav if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */ 96571047Sjulian _NG_NODE_UNREF((_NG_HOOK_NODE(hook))); 96670700Sjulian NG_FREE_HOOK(hook); 96770700Sjulian } 96852419Sjulian} 96952419Sjulian 97052419Sjulian/* 97152419Sjulian * Add an unconnected hook to a node. Only used internally. 97270939Sjulian * Assumes node is locked. (XXX not yet true ) 97352419Sjulian */ 97452419Sjulianstatic int 97552419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp) 97652419Sjulian{ 97752419Sjulian hook_p hook; 97852419Sjulian int error = 0; 97952419Sjulian 98052419Sjulian /* Check that the given name is good */ 98152419Sjulian if (name == NULL) { 98271047Sjulian TRAP_ERROR(); 98352419Sjulian return (EINVAL); 98452419Sjulian } 98554096Sarchie if (ng_findhook(node, name) != NULL) { 98671047Sjulian TRAP_ERROR(); 98754096Sarchie return (EEXIST); 98852419Sjulian } 98952419Sjulian 99052419Sjulian /* Allocate the hook and link it up */ 99170784Sjulian NG_ALLOC_HOOK(hook); 99252419Sjulian if (hook == NULL) { 99371047Sjulian TRAP_ERROR(); 99452419Sjulian return (ENOMEM); 99552419Sjulian } 99670939Sjulian hook->hk_refs = 1; /* add a reference for us to return */ 99770784Sjulian hook->hk_flags = HK_INVALID; 99870939Sjulian hook->hk_peer = &ng_deadhook; /* start off this way */ 99970784Sjulian hook->hk_node = node; 100070784Sjulian NG_NODE_REF(node); /* each hook counts as a reference */ 100152419Sjulian 100270939Sjulian /* Set hook name */ 1003125028Sharti strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ); 100470939Sjulian 100570939Sjulian /* 100670939Sjulian * Check if the node type code has something to say about it 100770939Sjulian * If it fails, the unref of the hook will also unref the node. 100870939Sjulian */ 100970935Sjulian if (node->nd_type->newhook != NULL) { 101070935Sjulian if ((error = (*node->nd_type->newhook)(node, hook, name))) { 101170784Sjulian NG_HOOK_UNREF(hook); /* this frees the hook */ 101270700Sjulian return (error); 101370700Sjulian } 101470935Sjulian } 101552419Sjulian /* 101652419Sjulian * The 'type' agrees so far, so go ahead and link it in. 101752419Sjulian * We'll ask again later when we actually connect the hooks. 101852419Sjulian */ 101970784Sjulian LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 102070784Sjulian node->nd_numhooks++; 102170939Sjulian NG_HOOK_REF(hook); /* one for the node */ 102252419Sjulian 102352419Sjulian if (hookp) 102452419Sjulian *hookp = hook; 102570939Sjulian return (0); 102652419Sjulian} 102752419Sjulian 102852419Sjulian/* 102954096Sarchie * Find a hook 103054096Sarchie * 103154096Sarchie * Node types may supply their own optimized routines for finding 103254096Sarchie * hooks. If none is supplied, we just do a linear search. 103370939Sjulian * XXX Possibly we should add a reference to the hook? 103454096Sarchie */ 103554096Sarchiehook_p 103654096Sarchieng_findhook(node_p node, const char *name) 103754096Sarchie{ 103854096Sarchie hook_p hook; 103954096Sarchie 104070784Sjulian if (node->nd_type->findhook != NULL) 104170784Sjulian return (*node->nd_type->findhook)(node, name); 104270784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 104370912Sjulian if (NG_HOOK_IS_VALID(hook) 104470917Sarchie && (strcmp(NG_HOOK_NAME(hook), name) == 0)) 104554096Sarchie return (hook); 104654096Sarchie } 104754096Sarchie return (NULL); 104854096Sarchie} 104954096Sarchie 105054096Sarchie/* 105152419Sjulian * Destroy a hook 105252419Sjulian * 105352419Sjulian * As hooks are always attached, this really destroys two hooks. 105452419Sjulian * The one given, and the one attached to it. Disconnect the hooks 105570939Sjulian * from each other first. We reconnect the peer hook to the 'dead' 105670939Sjulian * hook so that it can still exist after we depart. We then 105770939Sjulian * send the peer its own destroy message. This ensures that we only 1058152451Sglebius * interact with the peer's structures when it is locked processing that 105970939Sjulian * message. We hold a reference to the peer hook so we are guaranteed that 106070939Sjulian * the peer hook and node are still going to exist until 106170939Sjulian * we are finished there as the hook holds a ref on the node. 1062152451Sglebius * We run this same code again on the peer hook, but that time it is already 1063152451Sglebius * attached to the 'dead' hook. 106471047Sjulian * 1065152451Sglebius * This routine is called at all stages of hook creation 106671047Sjulian * on error detection and must be able to handle any such stage. 106752419Sjulian */ 106852419Sjulianvoid 106952419Sjulianng_destroy_hook(hook_p hook) 107052419Sjulian{ 1071151974Sglebius hook_p peer; 1072151974Sglebius node_p node; 107352419Sjulian 107471047Sjulian if (hook == &ng_deadhook) { /* better safe than sorry */ 107571047Sjulian printf("ng_destroy_hook called on deadhook\n"); 107671047Sjulian return; 107771047Sjulian } 1078151974Sglebius 1079151974Sglebius /* 1080151974Sglebius * Protect divorce process with mutex, to avoid races on 1081151974Sglebius * simultaneous disconnect. 1082151974Sglebius */ 1083151974Sglebius mtx_lock(&ng_topo_mtx); 1084151974Sglebius 1085151974Sglebius hook->hk_flags |= HK_INVALID; 1086151974Sglebius 1087151974Sglebius peer = NG_HOOK_PEER(hook); 1088151974Sglebius node = NG_HOOK_NODE(hook); 1089151974Sglebius 109070939Sjulian if (peer && (peer != &ng_deadhook)) { 109170939Sjulian /* 109270939Sjulian * Set the peer to point to ng_deadhook 109370939Sjulian * from this moment on we are effectively independent it. 109470939Sjulian * send it an rmhook message of it's own. 109570939Sjulian */ 109670939Sjulian peer->hk_peer = &ng_deadhook; /* They no longer know us */ 109770939Sjulian hook->hk_peer = &ng_deadhook; /* Nor us, them */ 109871047Sjulian if (NG_HOOK_NODE(peer) == &ng_deadnode) { 1099152451Sglebius /* 110071047Sjulian * If it's already divorced from a node, 110171047Sjulian * just free it. 110271047Sjulian */ 1103151974Sglebius mtx_unlock(&ng_topo_mtx); 110471047Sjulian } else { 1105151974Sglebius mtx_unlock(&ng_topo_mtx); 110671047Sjulian ng_rmhook_self(peer); /* Send it a surprise */ 110771047Sjulian } 110870942Sjulian NG_HOOK_UNREF(peer); /* account for peer link */ 110970942Sjulian NG_HOOK_UNREF(hook); /* account for peer link */ 1110151974Sglebius } else 1111151974Sglebius mtx_unlock(&ng_topo_mtx); 111252419Sjulian 1113151974Sglebius mtx_assert(&ng_topo_mtx, MA_NOTOWNED); 1114151974Sglebius 111552419Sjulian /* 111652419Sjulian * Remove the hook from the node's list to avoid possible recursion 111752419Sjulian * in case the disconnection results in node shutdown. 111852419Sjulian */ 111971047Sjulian if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */ 112071047Sjulian return; 112171047Sjulian } 112270784Sjulian LIST_REMOVE(hook, hk_hooks); 112370784Sjulian node->nd_numhooks--; 112470784Sjulian if (node->nd_type->disconnect) { 112552419Sjulian /* 112671047Sjulian * The type handler may elect to destroy the node so don't 1127167677Srwatson * trust its existence after this point. (except 112871047Sjulian * that we still hold a reference on it. (which we 112971047Sjulian * inherrited from the hook we are destroying) 113052419Sjulian */ 113170784Sjulian (*node->nd_type->disconnect) (hook); 113252419Sjulian } 113371047Sjulian 113471047Sjulian /* 113571047Sjulian * Note that because we will point to ng_deadnode, the original node 113671047Sjulian * is not decremented automatically so we do that manually. 113771047Sjulian */ 113871047Sjulian _NG_HOOK_NODE(hook) = &ng_deadnode; 113971047Sjulian NG_NODE_UNREF(node); /* We no longer point to it so adjust count */ 114071047Sjulian NG_HOOK_UNREF(hook); /* Account for linkage (in list) to node */ 114152419Sjulian} 114252419Sjulian 114352419Sjulian/* 114452419Sjulian * Take two hooks on a node and merge the connection so that the given node 114552419Sjulian * is effectively bypassed. 114652419Sjulian */ 114752419Sjulianint 114852419Sjulianng_bypass(hook_p hook1, hook_p hook2) 114952419Sjulian{ 115070784Sjulian if (hook1->hk_node != hook2->hk_node) { 115171047Sjulian TRAP_ERROR(); 115252419Sjulian return (EINVAL); 115370784Sjulian } 115470784Sjulian hook1->hk_peer->hk_peer = hook2->hk_peer; 115570784Sjulian hook2->hk_peer->hk_peer = hook1->hk_peer; 115652419Sjulian 115770939Sjulian hook1->hk_peer = &ng_deadhook; 115870939Sjulian hook2->hk_peer = &ng_deadhook; 115970939Sjulian 1160163244Sglebius NG_HOOK_UNREF(hook1); 1161163244Sglebius NG_HOOK_UNREF(hook2); 1162163244Sglebius 116352419Sjulian /* XXX If we ever cache methods on hooks update them as well */ 116452419Sjulian ng_destroy_hook(hook1); 116552419Sjulian ng_destroy_hook(hook2); 116652419Sjulian return (0); 116752419Sjulian} 116852419Sjulian 116952419Sjulian/* 117052419Sjulian * Install a new netgraph type 117152419Sjulian */ 117252419Sjulianint 117352419Sjulianng_newtype(struct ng_type *tp) 117452419Sjulian{ 117552419Sjulian const size_t namelen = strlen(tp->name); 117652419Sjulian 117752419Sjulian /* Check version and type name fields */ 117870159Sjulian if ((tp->version != NG_ABI_VERSION) 117970159Sjulian || (namelen == 0) 1180125028Sharti || (namelen >= NG_TYPESIZ)) { 118171047Sjulian TRAP_ERROR(); 1182131374Sjulian if (tp->version != NG_ABI_VERSION) { 1183131374Sjulian printf("Netgraph: Node type rejected. ABI mismatch. Suggest recompile\n"); 1184131374Sjulian } 118552419Sjulian return (EINVAL); 118652419Sjulian } 118752419Sjulian 118852419Sjulian /* Check for name collision */ 118952419Sjulian if (ng_findtype(tp->name) != NULL) { 119071047Sjulian TRAP_ERROR(); 119152419Sjulian return (EEXIST); 119252419Sjulian } 119352419Sjulian 119470700Sjulian 119552419Sjulian /* Link in new type */ 119672200Sbmilekic mtx_lock(&ng_typelist_mtx); 119770700Sjulian LIST_INSERT_HEAD(&ng_typelist, tp, types); 119871603Sjulian tp->refs = 1; /* first ref is linked list */ 119972200Sbmilekic mtx_unlock(&ng_typelist_mtx); 120052419Sjulian return (0); 120152419Sjulian} 120252419Sjulian 120352419Sjulian/* 120480222Sjulian * unlink a netgraph type 120580222Sjulian * If no examples exist 120680222Sjulian */ 120780222Sjulianint 120880222Sjulianng_rmtype(struct ng_type *tp) 120980222Sjulian{ 121080222Sjulian /* Check for name collision */ 121180222Sjulian if (tp->refs != 1) { 121280222Sjulian TRAP_ERROR(); 121380222Sjulian return (EBUSY); 121480222Sjulian } 121580222Sjulian 121680222Sjulian /* Unlink type */ 121780222Sjulian mtx_lock(&ng_typelist_mtx); 121880222Sjulian LIST_REMOVE(tp, types); 121980222Sjulian mtx_unlock(&ng_typelist_mtx); 122080222Sjulian return (0); 122180222Sjulian} 122280222Sjulian 122380222Sjulian/* 122452419Sjulian * Look for a type of the name given 122552419Sjulian */ 122652419Sjulianstruct ng_type * 122752419Sjulianng_findtype(const char *typename) 122852419Sjulian{ 122952419Sjulian struct ng_type *type; 123052419Sjulian 123172200Sbmilekic mtx_lock(&ng_typelist_mtx); 123270700Sjulian LIST_FOREACH(type, &ng_typelist, types) { 123352419Sjulian if (strcmp(type->name, typename) == 0) 123452419Sjulian break; 123552419Sjulian } 123672200Sbmilekic mtx_unlock(&ng_typelist_mtx); 123752419Sjulian return (type); 123852419Sjulian} 123952419Sjulian 124052419Sjulian/************************************************************************ 124152419Sjulian Composite routines 124252419Sjulian************************************************************************/ 124352419Sjulian/* 124471047Sjulian * Connect two nodes using the specified hooks, using queued functions. 124552419Sjulian */ 1246172806Smavstatic int 1247172806Smavng_con_part3(node_p node, item_p item, hook_p hook) 124852419Sjulian{ 1249172806Smav int error = 0; 125052419Sjulian 125171047Sjulian /* 125271047Sjulian * When we run, we know that the node 'node' is locked for us. 125371047Sjulian * Our caller has a reference on the hook. 125471047Sjulian * Our caller has a reference on the node. 125571047Sjulian * (In this case our caller is ng_apply_item() ). 125671047Sjulian * The peer hook has a reference on the hook. 125771849Sjulian * We are all set up except for the final call to the node, and 125871849Sjulian * the clearing of the INVALID flag. 125971047Sjulian */ 126071047Sjulian if (NG_HOOK_NODE(hook) == &ng_deadnode) { 126171047Sjulian /* 126271047Sjulian * The node must have been freed again since we last visited 126371047Sjulian * here. ng_destry_hook() has this effect but nothing else does. 126471047Sjulian * We should just release our references and 126571047Sjulian * free anything we can think of. 126671047Sjulian * Since we know it's been destroyed, and it's our caller 126771047Sjulian * that holds the references, just return. 126871047Sjulian */ 1269172806Smav ERROUT(ENOENT); 127052419Sjulian } 127171047Sjulian if (hook->hk_node->nd_type->connect) { 1272172806Smav if ((error = (*hook->hk_node->nd_type->connect) (hook))) { 127371047Sjulian ng_destroy_hook(hook); /* also zaps peer */ 127471849Sjulian printf("failed in ng_con_part3()\n"); 1275172806Smav ERROUT(error); 127671047Sjulian } 127752419Sjulian } 127871047Sjulian /* 127971047Sjulian * XXX this is wrong for SMP. Possibly we need 128071047Sjulian * to separate out 'create' and 'invalid' flags. 128171047Sjulian * should only set flags on hooks we have locked under our node. 128271047Sjulian */ 128371047Sjulian hook->hk_flags &= ~HK_INVALID; 1284172806Smavdone: 1285172806Smav NG_FREE_ITEM(item); 1286172806Smav return (error); 128771047Sjulian} 128852419Sjulian 1289172806Smavstatic int 1290172806Smavng_con_part2(node_p node, item_p item, hook_p hook) 129171047Sjulian{ 1292172806Smav hook_p peer; 1293172806Smav int error = 0; 129471047Sjulian 129552419Sjulian /* 129671047Sjulian * When we run, we know that the node 'node' is locked for us. 129771047Sjulian * Our caller has a reference on the hook. 129871047Sjulian * Our caller has a reference on the node. 129971047Sjulian * (In this case our caller is ng_apply_item() ). 130071047Sjulian * The peer hook has a reference on the hook. 130171047Sjulian * our node pointer points to the 'dead' node. 130271047Sjulian * First check the hook name is unique. 130371849Sjulian * Should not happen because we checked before queueing this. 130452419Sjulian */ 130571047Sjulian if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) { 130671047Sjulian TRAP_ERROR(); 130771047Sjulian ng_destroy_hook(hook); /* should destroy peer too */ 130871849Sjulian printf("failed in ng_con_part2()\n"); 1309172806Smav ERROUT(EEXIST); 131071047Sjulian } 131170939Sjulian /* 131271047Sjulian * Check if the node type code has something to say about it 131371047Sjulian * If it fails, the unref of the hook will also unref the attached node, 131471047Sjulian * however since that node is 'ng_deadnode' this will do nothing. 131571047Sjulian * The peer hook will also be destroyed. 131670939Sjulian */ 131771047Sjulian if (node->nd_type->newhook != NULL) { 1318172806Smav if ((error = (*node->nd_type->newhook)(node, hook, 1319172806Smav hook->hk_name))) { 132071047Sjulian ng_destroy_hook(hook); /* should destroy peer too */ 132171849Sjulian printf("failed in ng_con_part2()\n"); 1322172806Smav ERROUT(error); 132371047Sjulian } 132471047Sjulian } 132571047Sjulian 132671047Sjulian /* 132771047Sjulian * The 'type' agrees so far, so go ahead and link it in. 132871047Sjulian * We'll ask again later when we actually connect the hooks. 132971047Sjulian */ 133071047Sjulian hook->hk_node = node; /* just overwrite ng_deadnode */ 133171047Sjulian NG_NODE_REF(node); /* each hook counts as a reference */ 133271047Sjulian LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 133371047Sjulian node->nd_numhooks++; 133471047Sjulian NG_HOOK_REF(hook); /* one for the node */ 133571047Sjulian 133671047Sjulian /* 1337167677Srwatson * We now have a symmetrical situation, where both hooks have been 133874078Sjulian * linked to their nodes, the newhook methods have been called 133971047Sjulian * And the references are all correct. The hooks are still marked 134071047Sjulian * as invalid, as we have not called the 'connect' methods 134171047Sjulian * yet. 1342167677Srwatson * We can call the local one immediately as we have the 134371047Sjulian * node locked, but we need to queue the remote one. 134471047Sjulian */ 134571047Sjulian if (hook->hk_node->nd_type->connect) { 1346172806Smav if ((error = (*hook->hk_node->nd_type->connect) (hook))) { 134771047Sjulian ng_destroy_hook(hook); /* also zaps peer */ 134871849Sjulian printf("failed in ng_con_part2(A)\n"); 1349172806Smav ERROUT(error); 135071047Sjulian } 135171047Sjulian } 1352151974Sglebius 1353151974Sglebius /* 1354151974Sglebius * Acquire topo mutex to avoid race with ng_destroy_hook(). 1355151974Sglebius */ 1356151974Sglebius mtx_lock(&ng_topo_mtx); 1357151974Sglebius peer = hook->hk_peer; 1358151974Sglebius if (peer == &ng_deadhook) { 1359151974Sglebius mtx_unlock(&ng_topo_mtx); 1360151974Sglebius printf("failed in ng_con_part2(B)\n"); 1361151974Sglebius ng_destroy_hook(hook); 1362172806Smav ERROUT(ENOENT); 1363151974Sglebius } 1364151974Sglebius mtx_unlock(&ng_topo_mtx); 1365151974Sglebius 1366173605Sglebius if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3, 1367173605Sglebius NULL, 0, NG_REUSE_ITEM))) { 1368151974Sglebius printf("failed in ng_con_part2(C)\n"); 136971849Sjulian ng_destroy_hook(hook); /* also zaps peer */ 1370172806Smav return (error); /* item was consumed. */ 137171849Sjulian } 137271047Sjulian hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */ 1373172806Smav return (0); /* item was consumed. */ 1374172806Smavdone: 1375172806Smav NG_FREE_ITEM(item); 1376172806Smav return (error); 137752419Sjulian} 137852419Sjulian 137952419Sjulian/* 1380152451Sglebius * Connect this node with another node. We assume that this node is 138171047Sjulian * currently locked, as we are only called from an NGM_CONNECT message. 138252419Sjulian */ 138371047Sjulianstatic int 1384172806Smavng_con_nodes(item_p item, node_p node, const char *name, 1385172806Smav node_p node2, const char *name2) 138652419Sjulian{ 1387152451Sglebius int error; 1388152451Sglebius hook_p hook; 1389152451Sglebius hook_p hook2; 139052419Sjulian 139171849Sjulian if (ng_findhook(node2, name2) != NULL) { 139271849Sjulian return(EEXIST); 139371849Sjulian } 139470939Sjulian if ((error = ng_add_hook(node, name, &hook))) /* gives us a ref */ 139552419Sjulian return (error); 139671047Sjulian /* Allocate the other hook and link it up */ 139771047Sjulian NG_ALLOC_HOOK(hook2); 1398140737Sglebius if (hook2 == NULL) { 139971047Sjulian TRAP_ERROR(); 140071047Sjulian ng_destroy_hook(hook); /* XXX check ref counts so far */ 140171047Sjulian NG_HOOK_UNREF(hook); /* including our ref */ 140271047Sjulian return (ENOMEM); 140371047Sjulian } 140471047Sjulian hook2->hk_refs = 1; /* start with a reference for us. */ 140571047Sjulian hook2->hk_flags = HK_INVALID; 140671047Sjulian hook2->hk_peer = hook; /* Link the two together */ 140771047Sjulian hook->hk_peer = hook2; 140871047Sjulian NG_HOOK_REF(hook); /* Add a ref for the peer to each*/ 140971047Sjulian NG_HOOK_REF(hook2); 1410152451Sglebius hook2->hk_node = &ng_deadnode; 1411125028Sharti strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ); 141271047Sjulian 141371047Sjulian /* 141471047Sjulian * Queue the function above. 141571047Sjulian * Procesing continues in that function in the lock context of 141671047Sjulian * the other node. 141771047Sjulian */ 1418173605Sglebius if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0, 1419173605Sglebius NG_NOFLAGS))) { 1420171885Smav printf("failed in ng_con_nodes(): %d\n", error); 1421171885Smav ng_destroy_hook(hook); /* also zaps peer */ 1422171885Smav } 142371047Sjulian 142471047Sjulian NG_HOOK_UNREF(hook); /* Let each hook go if it wants to */ 142571047Sjulian NG_HOOK_UNREF(hook2); 1426171885Smav return (error); 142771047Sjulian} 142871047Sjulian 142971047Sjulian/* 143071047Sjulian * Make a peer and connect. 143171047Sjulian * We assume that the local node is locked. 143271047Sjulian * The new node probably doesn't need a lock until 143371047Sjulian * it has a hook, because it cannot really have any work until then, 143471047Sjulian * but we should think about it a bit more. 143571047Sjulian * 143671047Sjulian * The problem may come if the other node also fires up 143771047Sjulian * some hardware or a timer or some other source of activation, 143871047Sjulian * also it may already get a command msg via it's ID. 143971047Sjulian * 144071047Sjulian * We could use the same method as ng_con_nodes() but we'd have 1441152451Sglebius * to add ability to remove the node when failing. (Not hard, just 144271047Sjulian * make arg1 point to the node to remove). 144371047Sjulian * Unless of course we just ignore failure to connect and leave 144471047Sjulian * an unconnected node? 144571047Sjulian */ 144671047Sjulianstatic int 144771047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type) 144871047Sjulian{ 1449152451Sglebius node_p node2; 1450152451Sglebius hook_p hook1, hook2; 1451152451Sglebius int error; 145271047Sjulian 145371047Sjulian if ((error = ng_make_node(type, &node2))) { 145452419Sjulian return (error); 145552419Sjulian } 145670939Sjulian 145771047Sjulian if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */ 145871849Sjulian ng_rmnode(node2, NULL, NULL, 0); 145971047Sjulian return (error); 146071047Sjulian } 146171047Sjulian 146271047Sjulian if ((error = ng_add_hook(node2, name2, &hook2))) { 146371849Sjulian ng_rmnode(node2, NULL, NULL, 0); 146471047Sjulian ng_destroy_hook(hook1); 146571047Sjulian NG_HOOK_UNREF(hook1); 146671047Sjulian return (error); 146771047Sjulian } 146871047Sjulian 146970939Sjulian /* 147071047Sjulian * Actually link the two hooks together. 147171047Sjulian */ 147271047Sjulian hook1->hk_peer = hook2; 147371047Sjulian hook2->hk_peer = hook1; 147471047Sjulian 147571047Sjulian /* Each hook is referenced by the other */ 147671047Sjulian NG_HOOK_REF(hook1); 147771047Sjulian NG_HOOK_REF(hook2); 147871047Sjulian 147971047Sjulian /* Give each node the opportunity to veto the pending connection */ 148071047Sjulian if (hook1->hk_node->nd_type->connect) { 148171047Sjulian error = (*hook1->hk_node->nd_type->connect) (hook1); 148271047Sjulian } 148371047Sjulian 148471047Sjulian if ((error == 0) && hook2->hk_node->nd_type->connect) { 148571047Sjulian error = (*hook2->hk_node->nd_type->connect) (hook2); 148671047Sjulian 148771047Sjulian } 148871047Sjulian 148971047Sjulian /* 149070939Sjulian * drop the references we were holding on the two hooks. 149170939Sjulian */ 149271047Sjulian if (error) { 149371047Sjulian ng_destroy_hook(hook2); /* also zaps hook1 */ 149471849Sjulian ng_rmnode(node2, NULL, NULL, 0); 149571047Sjulian } else { 149671047Sjulian /* As a last act, allow the hooks to be used */ 149771047Sjulian hook1->hk_flags &= ~HK_INVALID; 149871047Sjulian hook2->hk_flags &= ~HK_INVALID; 149971047Sjulian } 150071047Sjulian NG_HOOK_UNREF(hook1); 150170939Sjulian NG_HOOK_UNREF(hook2); 150270939Sjulian return (error); 150352419Sjulian} 150471047Sjulian 150570700Sjulian/************************************************************************ 150670700Sjulian Utility routines to send self messages 150770700Sjulian************************************************************************/ 150870700Sjulian 150971849Sjulian/* Shut this node down as soon as everyone is clear of it */ 1510167677Srwatson/* Should add arg "immediately" to jump the queue */ 151170700Sjulianint 151271849Sjulianng_rmnode_self(node_p node) 151370700Sjulian{ 151471849Sjulian int error; 151552419Sjulian 151671849Sjulian if (node == &ng_deadnode) 151771849Sjulian return (0); 1518132464Sjulian node->nd_flags |= NGF_INVALID; 1519132464Sjulian if (node->nd_flags & NGF_CLOSING) 152071849Sjulian return (0); 152170700Sjulian 152271849Sjulian error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0); 152371849Sjulian return (error); 152470700Sjulian} 152570700Sjulian 152671849Sjulianstatic void 152771047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2) 152871047Sjulian{ 152971047Sjulian ng_destroy_hook(hook); 153071849Sjulian return ; 153171047Sjulian} 153271047Sjulian 153370935Sjulianint 153470935Sjulianng_rmhook_self(hook_p hook) 153570935Sjulian{ 153671047Sjulian int error; 153770935Sjulian node_p node = NG_HOOK_NODE(hook); 153870935Sjulian 153971047Sjulian if (node == &ng_deadnode) 154071047Sjulian return (0); 154171047Sjulian 154271047Sjulian error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0); 154371047Sjulian return (error); 154470935Sjulian} 154570935Sjulian 154670700Sjulian/*********************************************************************** 154752419Sjulian * Parse and verify a string of the form: <NODE:><PATH> 154852419Sjulian * 154952419Sjulian * Such a string can refer to a specific node or a specific hook 155052419Sjulian * on a specific node, depending on how you look at it. In the 155152419Sjulian * latter case, the PATH component must not end in a dot. 155252419Sjulian * 155352419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string 155452419Sjulian * of hook names separated by dots. This breaks out the original 155552419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp 155652419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to 155752419Sjulian * the final hook component of <PATH>, if any, otherwise NULL. 155852419Sjulian * 155952419Sjulian * This returns -1 if the path is malformed. The char ** are optional. 156070700Sjulian ***********************************************************************/ 156152419Sjulianint 156252419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 156352419Sjulian{ 1564152451Sglebius char *node, *path, *hook; 1565152451Sglebius int k; 156652419Sjulian 156752419Sjulian /* 156852419Sjulian * Extract absolute NODE, if any 156952419Sjulian */ 157052419Sjulian for (path = addr; *path && *path != ':'; path++); 157152419Sjulian if (*path) { 157252419Sjulian node = addr; /* Here's the NODE */ 157352419Sjulian *path++ = '\0'; /* Here's the PATH */ 157452419Sjulian 157552419Sjulian /* Node name must not be empty */ 157652419Sjulian if (!*node) 157752419Sjulian return -1; 157852419Sjulian 157952419Sjulian /* A name of "." is OK; otherwise '.' not allowed */ 158052419Sjulian if (strcmp(node, ".") != 0) { 158152419Sjulian for (k = 0; node[k]; k++) 158252419Sjulian if (node[k] == '.') 158352419Sjulian return -1; 158452419Sjulian } 158552419Sjulian } else { 158652419Sjulian node = NULL; /* No absolute NODE */ 158752419Sjulian path = addr; /* Here's the PATH */ 158852419Sjulian } 158952419Sjulian 159052419Sjulian /* Snoop for illegal characters in PATH */ 159152419Sjulian for (k = 0; path[k]; k++) 159252419Sjulian if (path[k] == ':') 159352419Sjulian return -1; 159452419Sjulian 159552419Sjulian /* Check for no repeated dots in PATH */ 159652419Sjulian for (k = 0; path[k]; k++) 159752419Sjulian if (path[k] == '.' && path[k + 1] == '.') 159852419Sjulian return -1; 159952419Sjulian 160052419Sjulian /* Remove extra (degenerate) dots from beginning or end of PATH */ 160152419Sjulian if (path[0] == '.') 160252419Sjulian path++; 160352419Sjulian if (*path && path[strlen(path) - 1] == '.') 160452419Sjulian path[strlen(path) - 1] = 0; 160552419Sjulian 160652419Sjulian /* If PATH has a dot, then we're not talking about a hook */ 160752419Sjulian if (*path) { 160852419Sjulian for (hook = path, k = 0; path[k]; k++) 160952419Sjulian if (path[k] == '.') { 161052419Sjulian hook = NULL; 161152419Sjulian break; 161252419Sjulian } 161352419Sjulian } else 161452419Sjulian path = hook = NULL; 161552419Sjulian 161652419Sjulian /* Done */ 161752419Sjulian if (nodep) 161852419Sjulian *nodep = node; 161952419Sjulian if (pathp) 162052419Sjulian *pathp = path; 162152419Sjulian if (hookp) 162252419Sjulian *hookp = hook; 162352419Sjulian return (0); 162452419Sjulian} 162552419Sjulian 162652419Sjulian/* 162752419Sjulian * Given a path, which may be absolute or relative, and a starting node, 162870700Sjulian * return the destination node. 162952419Sjulian */ 163052419Sjulianint 163170700Sjulianng_path2noderef(node_p here, const char *address, 163270700Sjulian node_p *destp, hook_p *lasthook) 163352419Sjulian{ 1634125028Sharti char fullpath[NG_PATHSIZ]; 163552419Sjulian char *nodename, *path, pbuf[2]; 163670700Sjulian node_p node, oldnode; 163752419Sjulian char *cp; 163859728Sjulian hook_p hook = NULL; 163952419Sjulian 164052419Sjulian /* Initialize */ 164170784Sjulian if (destp == NULL) { 164271047Sjulian TRAP_ERROR(); 164352419Sjulian return EINVAL; 164470784Sjulian } 164552419Sjulian *destp = NULL; 164652419Sjulian 164752419Sjulian /* Make a writable copy of address for ng_path_parse() */ 164852419Sjulian strncpy(fullpath, address, sizeof(fullpath) - 1); 164952419Sjulian fullpath[sizeof(fullpath) - 1] = '\0'; 165052419Sjulian 165152419Sjulian /* Parse out node and sequence of hooks */ 165252419Sjulian if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 165371047Sjulian TRAP_ERROR(); 165452419Sjulian return EINVAL; 165552419Sjulian } 165652419Sjulian if (path == NULL) { 165752419Sjulian pbuf[0] = '.'; /* Needs to be writable */ 165852419Sjulian pbuf[1] = '\0'; 165952419Sjulian path = pbuf; 166052419Sjulian } 166152419Sjulian 166270700Sjulian /* 166370700Sjulian * For an absolute address, jump to the starting node. 166470700Sjulian * Note that this holds a reference on the node for us. 166570700Sjulian * Don't forget to drop the reference if we don't need it. 166670700Sjulian */ 166752419Sjulian if (nodename) { 166870700Sjulian node = ng_name2noderef(here, nodename); 166952419Sjulian if (node == NULL) { 167071047Sjulian TRAP_ERROR(); 167152419Sjulian return (ENOENT); 167252419Sjulian } 167370700Sjulian } else { 167470700Sjulian if (here == NULL) { 167571047Sjulian TRAP_ERROR(); 167670700Sjulian return (EINVAL); 167770700Sjulian } 167852419Sjulian node = here; 167970784Sjulian NG_NODE_REF(node); 168070700Sjulian } 168152419Sjulian 168270700Sjulian /* 1683152451Sglebius * Now follow the sequence of hooks 168470700Sjulian * XXX 168570700Sjulian * We actually cannot guarantee that the sequence 168670700Sjulian * is not being demolished as we crawl along it 168770700Sjulian * without extra-ordinary locking etc. 168870700Sjulian * So this is a bit dodgy to say the least. 168970700Sjulian * We can probably hold up some things by holding 169070700Sjulian * the nodelist mutex for the time of this 169170700Sjulian * crawl if we wanted.. At least that way we wouldn't have to 1692167677Srwatson * worry about the nodes disappearing, but the hooks would still 169370700Sjulian * be a problem. 169470700Sjulian */ 169552419Sjulian for (cp = path; node != NULL && *cp != '\0'; ) { 169652419Sjulian char *segment; 169752419Sjulian 169852419Sjulian /* 169952419Sjulian * Break out the next path segment. Replace the dot we just 170052419Sjulian * found with a NUL; "cp" points to the next segment (or the 170152419Sjulian * NUL at the end). 170252419Sjulian */ 170352419Sjulian for (segment = cp; *cp != '\0'; cp++) { 170452419Sjulian if (*cp == '.') { 170552419Sjulian *cp++ = '\0'; 170652419Sjulian break; 170752419Sjulian } 170852419Sjulian } 170952419Sjulian 171052419Sjulian /* Empty segment */ 171152419Sjulian if (*segment == '\0') 171252419Sjulian continue; 171352419Sjulian 171452419Sjulian /* We have a segment, so look for a hook by that name */ 171554096Sarchie hook = ng_findhook(node, segment); 171652419Sjulian 171752419Sjulian /* Can't get there from here... */ 171852419Sjulian if (hook == NULL 171970784Sjulian || NG_HOOK_PEER(hook) == NULL 172070784Sjulian || NG_HOOK_NOT_VALID(hook) 172170784Sjulian || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) { 172271047Sjulian TRAP_ERROR(); 172370784Sjulian NG_NODE_UNREF(node); 1724152451Sglebius#if 0 172570784Sjulian printf("hooknotvalid %s %s %d %d %d %d ", 172670784Sjulian path, 172770784Sjulian segment, 172870784Sjulian hook == NULL, 1729152451Sglebius NG_HOOK_PEER(hook) == NULL, 1730152451Sglebius NG_HOOK_NOT_VALID(hook), 1731152451Sglebius NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))); 173270784Sjulian#endif 173352419Sjulian return (ENOENT); 173452419Sjulian } 173552419Sjulian 173670700Sjulian /* 1737152451Sglebius * Hop on over to the next node 173870700Sjulian * XXX 1739152451Sglebius * Big race conditions here as hooks and nodes go away 174070700Sjulian * *** Idea.. store an ng_ID_t in each hook and use that 174170700Sjulian * instead of the direct hook in this crawl? 174270700Sjulian */ 174370700Sjulian oldnode = node; 174470784Sjulian if ((node = NG_PEER_NODE(hook))) 174570784Sjulian NG_NODE_REF(node); /* XXX RACE */ 174670784Sjulian NG_NODE_UNREF(oldnode); /* XXX another race */ 174770784Sjulian if (NG_NODE_NOT_VALID(node)) { 174870784Sjulian NG_NODE_UNREF(node); /* XXX more races */ 174970700Sjulian node = NULL; 175070700Sjulian } 175152419Sjulian } 175252419Sjulian 175352419Sjulian /* If node somehow missing, fail here (probably this is not needed) */ 175452419Sjulian if (node == NULL) { 175571047Sjulian TRAP_ERROR(); 175652419Sjulian return (ENXIO); 175752419Sjulian } 175852419Sjulian 175952419Sjulian /* Done */ 176052419Sjulian *destp = node; 176159900Sarchie if (lasthook != NULL) 176270784Sjulian *lasthook = (hook ? NG_HOOK_PEER(hook) : NULL); 176352419Sjulian return (0); 176452419Sjulian} 176552419Sjulian 176670700Sjulian/***************************************************************\ 176770700Sjulian* Input queue handling. 176870700Sjulian* All activities are submitted to the node via the input queue 176970700Sjulian* which implements a multiple-reader/single-writer gate. 1770167677Srwatson* Items which cannot be handled immediately are queued. 177170700Sjulian* 177270700Sjulian* read-write queue locking inline functions * 177370700Sjulian\***************************************************************/ 177470700Sjulian 1775151238Sglebiusstatic __inline item_p ng_dequeue(struct ng_queue * ngq, int *rw); 177670700Sjulianstatic __inline item_p ng_acquire_read(struct ng_queue * ngq, 177770700Sjulian item_p item); 177870700Sjulianstatic __inline item_p ng_acquire_write(struct ng_queue * ngq, 177970700Sjulian item_p item); 178070700Sjulianstatic __inline void ng_leave_read(struct ng_queue * ngq); 178170700Sjulianstatic __inline void ng_leave_write(struct ng_queue * ngq); 178270700Sjulianstatic __inline void ng_queue_rw(struct ng_queue * ngq, 178370700Sjulian item_p item, int rw); 178470700Sjulian 178552419Sjulian/* 178670700Sjulian * Definition of the bits fields in the ng_queue flag word. 178770700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle 178870700Sjulian * with them. 178970700Sjulian * 179071902Sjulian * The ordering here may be important! don't shuffle these. 179152419Sjulian */ 179270700Sjulian/*- 179370700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet) 179470700Sjulian | 179570700Sjulian V 179670700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+ 1797151973Sglebius | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1798151973Sglebius | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A| 1799151973Sglebius | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W| 180070700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+ 1801151973Sglebius \___________________________ ____________________________/ | | 1802151973Sglebius V | | 1803151973Sglebius [active reader count] | | 180470700Sjulian | | 1805151973Sglebius Operation Pending -------------------------------+ | 180670700Sjulian | 1807151973Sglebius Active Writer ---------------------------------------+ 180871902Sjulian 180971902Sjulian 181070700Sjulian*/ 1811151973Sglebius#define WRITER_ACTIVE 0x00000001 1812151973Sglebius#define OP_PENDING 0x00000002 1813151973Sglebius#define READER_INCREMENT 0x00000004 1814151973Sglebius#define READER_MASK 0xfffffffc /* Not valid if WRITER_ACTIVE is set */ 1815151973Sglebius#define SAFETY_BARRIER 0x00100000 /* 128K items queued should be enough */ 181671902Sjulian 181771902Sjulian/* Defines of more elaborate states on the queue */ 1818151973Sglebius/* Mask of bits a new read cares about */ 1819151973Sglebius#define NGQ_RMASK (WRITER_ACTIVE|OP_PENDING) 182071902Sjulian 1821151973Sglebius/* Mask of bits a new write cares about */ 182271902Sjulian#define NGQ_WMASK (NGQ_RMASK|READER_MASK) 182371902Sjulian 1824151973Sglebius/* Test to decide if there is something on the queue. */ 1825151973Sglebius#define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING) 182671902Sjulian 1827151973Sglebius/* How to decide what the next queued item is. */ 1828151973Sglebius#define HEAD_IS_READER(QP) NGI_QUEUED_READER((QP)->queue) 1829151973Sglebius#define HEAD_IS_WRITER(QP) NGI_QUEUED_WRITER((QP)->queue) /* notused */ 1830151973Sglebius 1831151973Sglebius/* Read the status to decide if the next item on the queue can now run. */ 1832151973Sglebius#define QUEUED_READER_CAN_PROCEED(QP) \ 1833151973Sglebius (((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0) 1834151973Sglebius#define QUEUED_WRITER_CAN_PROCEED(QP) \ 1835151973Sglebius (((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0) 1836151973Sglebius 183771902Sjulian/* Is there a chance of getting ANY work off the queue? */ 1838151973Sglebius#define NEXT_QUEUED_ITEM_CAN_PROCEED(QP) \ 1839151973Sglebius ((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) : \ 1840177953Smav QUEUED_WRITER_CAN_PROCEED(QP)) 184171902Sjulian 1842151973Sglebius 1843151238Sglebius#define NGQRW_R 0 1844151238Sglebius#define NGQRW_W 1 1845151238Sglebius 184670700Sjulian/* 184770700Sjulian * Taking into account the current state of the queue and node, possibly take 184870700Sjulian * the next entry off the queue and return it. Return NULL if there was 184970700Sjulian * nothing we could return, either because there really was nothing there, or 185070700Sjulian * because the node was in a state where it cannot yet process the next item 185170700Sjulian * on the queue. 185270700Sjulian * 185370700Sjulian * This MUST MUST MUST be called with the mutex held. 185470700Sjulian */ 185570700Sjulianstatic __inline item_p 1856151238Sglebiusng_dequeue(struct ng_queue *ngq, int *rw) 185770700Sjulian{ 185870700Sjulian item_p item; 185971902Sjulian 1860139039Sglebius mtx_assert(&ngq->q_mtx, MA_OWNED); 1861152451Sglebius /* 1862151973Sglebius * If there is nothing queued, then just return. 1863151973Sglebius * No point in continuing. 1864151973Sglebius */ 1865151973Sglebius if (!QUEUE_ACTIVE(ngq)) { 1866154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; " 1867154275Sglebius "queue flags 0x%lx", __func__, 1868154275Sglebius ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags); 1869151973Sglebius return (NULL); 1870151973Sglebius } 1871139039Sglebius 1872151973Sglebius /* 1873151973Sglebius * From here, we can assume there is a head item. 1874151973Sglebius * We need to find out what it is and if it can be dequeued, given 1875151973Sglebius * the current state of the node. 1876151973Sglebius */ 1877151973Sglebius if (HEAD_IS_READER(ngq)) { 1878177953Smav while (1) { 1879177953Smav long t = ngq->q_flags; 1880177953Smav if (t & WRITER_ACTIVE) { 1881177953Smav /* It's a reader but we can't use it. */ 1882177953Smav CTR4(KTR_NET, "%20s: node [%x] (%p) queued reader " 1883177953Smav "can't proceed; queue flags 0x%lx", __func__, 1884177953Smav ngq->q_node->nd_ID, ngq->q_node, t); 1885177953Smav return (NULL); 1886177953Smav } 1887177953Smav if (atomic_cmpset_long(&ngq->q_flags, t, 1888177953Smav t + READER_INCREMENT)) 1889177953Smav break; 1890177953Smav cpu_spinwait(); 1891151973Sglebius } 1892151238Sglebius *rw = NGQRW_R; 1893177953Smav } else if (atomic_cmpset_long(&ngq->q_flags, OP_PENDING, 1894177953Smav OP_PENDING + WRITER_ACTIVE)) { 1895151238Sglebius *rw = NGQRW_W; 189670700Sjulian } else { 1897177953Smav CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer " 1898177953Smav "can't proceed; queue flags 0x%lx", __func__, 1899154275Sglebius ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags); 1900151973Sglebius return (NULL); 190170700Sjulian } 190252419Sjulian 190370700Sjulian /* 190470700Sjulian * Now we dequeue the request (whatever it may be) and correct the 190570700Sjulian * pending flags and the next and last pointers. 190670700Sjulian */ 190770700Sjulian item = ngq->queue; 190870700Sjulian ngq->queue = item->el_next; 190970700Sjulian if (ngq->last == &(item->el_next)) { 191070700Sjulian ngq->last = &(ngq->queue); 1911177953Smav atomic_clear_long(&ngq->q_flags, OP_PENDING); 191270700Sjulian } 1913154275Sglebius CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; " 1914154275Sglebius "queue flags 0x%lx", __func__, 1915154275Sglebius ngq->q_node->nd_ID, ngq->q_node, item, *rw ? "WRITER" : "READER" , 1916154225Sglebius ngq->q_flags); 191770700Sjulian return (item); 191870700Sjulian} 191952419Sjulian 192070700Sjulian/* 192170700Sjulian * Queue a packet to be picked up by someone else. 192270700Sjulian * We really don't care who, but we can't or don't want to hang around 192370700Sjulian * to process it ourselves. We are probably an interrupt routine.. 1924151973Sglebius * If the queue could be run, flag the netisr handler to start. 192570700Sjulian */ 192670700Sjulianstatic __inline void 192770700Sjulianng_queue_rw(struct ng_queue * ngq, item_p item, int rw) 192870700Sjulian{ 1929151973Sglebius if (rw == NGQRW_W) 1930151973Sglebius NGI_SET_WRITER(item); 1931151973Sglebius else 1932151973Sglebius NGI_SET_READER(item); 193370700Sjulian item->el_next = NULL; /* maybe not needed */ 1934177953Smav 1935177953Smav NG_QUEUE_LOCK(ngq); 1936177953Smav /* Set OP_PENDING flag and enqueue the item. */ 1937177953Smav atomic_set_long(&ngq->q_flags, OP_PENDING); 193870700Sjulian *ngq->last = item; 1939177953Smav ngq->last = &(item->el_next); 1940177953Smav 1941154275Sglebius CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__, 1942154275Sglebius ngq->q_node->nd_ID, ngq->q_node, item, rw ? "WRITER" : "READER" ); 1943177953Smav 194470700Sjulian /* 1945151973Sglebius * We can take the worklist lock with the node locked 1946151973Sglebius * BUT NOT THE REVERSE! 1947151973Sglebius */ 1948151973Sglebius if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 1949177953Smav ng_worklist_add(ngq->q_node); 1950177953Smav NG_QUEUE_UNLOCK(ngq); 195170700Sjulian} 195252419Sjulian 195370700Sjulianstatic __inline item_p 195470700Sjulianng_acquire_read(struct ng_queue *ngq, item_p item) 195552419Sjulian{ 1956151974Sglebius KASSERT(ngq != &ng_deadnode.nd_input_queue, 1957151974Sglebius ("%s: working on deadnode", __func__)); 195852419Sjulian 1959177953Smav /* Reader needs node without writer and pending items. */ 1960177953Smav while (1) { 1961177953Smav long t = ngq->q_flags; 1962177953Smav if (t & NGQ_RMASK) 1963177953Smav break; /* Node is not ready for reader. */ 1964177953Smav if (atomic_cmpset_long(&ngq->q_flags, t, t + READER_INCREMENT)) { 1965177953Smav /* Successfully grabbed node */ 1966177953Smav CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", 1967177953Smav __func__, ngq->q_node->nd_ID, ngq->q_node, item); 1968177953Smav return (item); 1969177953Smav } 1970177953Smav cpu_spinwait(); 1971177953Smav }; 197270700Sjulian 1973177953Smav /* Queue the request for later. */ 197470700Sjulian ng_queue_rw(ngq, item, NGQRW_R); 197570700Sjulian 1976148236Sglebius return (NULL); 197770700Sjulian} 197870700Sjulian 197970700Sjulianstatic __inline item_p 198070700Sjulianng_acquire_write(struct ng_queue *ngq, item_p item) 198170700Sjulian{ 1982151974Sglebius KASSERT(ngq != &ng_deadnode.nd_input_queue, 1983151974Sglebius ("%s: working on deadnode", __func__)); 1984151974Sglebius 1985177953Smav /* Writer needs completely idle node. */ 1986177953Smav if (atomic_cmpset_long(&ngq->q_flags, 0, WRITER_ACTIVE)) { 1987177953Smav /* Successfully grabbed node */ 1988154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", 1989154275Sglebius __func__, ngq->q_node->nd_ID, ngq->q_node, item); 199070700Sjulian return (item); 199152419Sjulian } 199252419Sjulian 1993177953Smav /* Queue the request for later. */ 199470700Sjulian ng_queue_rw(ngq, item, NGQRW_W); 199570700Sjulian 1996148236Sglebius return (NULL); 199770700Sjulian} 199870700Sjulian 1999167385Sjulian#if 0 2000167385Sjulianstatic __inline item_p 2001167385Sjulianng_upgrade_write(struct ng_queue *ngq, item_p item) 2002167385Sjulian{ 2003167385Sjulian KASSERT(ngq != &ng_deadnode.nd_input_queue, 2004167385Sjulian ("%s: working on deadnode", __func__)); 2005167385Sjulian 2006167385Sjulian NGI_SET_WRITER(item); 2007167385Sjulian 2008167385Sjulian mtx_lock_spin(&(ngq->q_mtx)); 2009167385Sjulian 2010167385Sjulian /* 2011167385Sjulian * There will never be no readers as we are there ourselves. 2012167385Sjulian * Set the WRITER_ACTIVE flags ASAP to block out fast track readers. 2013167385Sjulian * The caller we are running from will call ng_leave_read() 2014167385Sjulian * soon, so we must account for that. We must leave again with the 2015167385Sjulian * READER lock. If we find other readers, then 2016167385Sjulian * queue the request for later. However "later" may be rignt now 2017167385Sjulian * if there are no readers. We don't really care if there are queued 2018167385Sjulian * items as we will bypass them anyhow. 2019167385Sjulian */ 2020167385Sjulian atomic_add_long(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT); 2021167385Sjulian if (ngq->q_flags & (NGQ_WMASK & ~OP_PENDING) == WRITER_ACTIVE) { 2022167385Sjulian mtx_unlock_spin(&(ngq->q_mtx)); 2023167385Sjulian 2024167385Sjulian /* It's just us, act on the item. */ 2025167385Sjulian /* will NOT drop writer lock when done */ 2026167385Sjulian ng_apply_item(node, item, 0); 2027167385Sjulian 2028167385Sjulian /* 2029167385Sjulian * Having acted on the item, atomically 2030167385Sjulian * down grade back to READER and finish up 2031167385Sjulian */ 2032167385Sjulian atomic_add_long(&ngq->q_flags, 2033167385Sjulian READER_INCREMENT - WRITER_ACTIVE); 2034167385Sjulian 2035167385Sjulian /* Our caller will call ng_leave_read() */ 2036167385Sjulian return; 2037167385Sjulian } 2038167385Sjulian /* 2039167385Sjulian * It's not just us active, so queue us AT THE HEAD. 2040167385Sjulian * "Why?" I hear you ask. 2041167385Sjulian * Put us at the head of the queue as we've already been 2042167385Sjulian * through it once. If there is nothing else waiting, 2043167385Sjulian * set the correct flags. 2044167385Sjulian */ 2045167385Sjulian if ((item->el_next = ngq->queue) == NULL) { 2046167385Sjulian /* 2047167385Sjulian * Set up the "last" pointer. 2048167385Sjulian * We are the only (and thus last) item 2049167385Sjulian */ 2050167385Sjulian ngq->last = &(item->el_next); 2051167385Sjulian 2052167385Sjulian /* We've gone from, 0 to 1 item in the queue */ 2053177953Smav atomic_set_long(&ngq->q_flags, OP_PENDING); 2054167385Sjulian 2055167385Sjulian CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__, 2056167385Sjulian ngq->q_node->nd_ID, ngq->q_node); 2057167385Sjulian }; 2058167385Sjulian ngq->queue = item; 2059167385Sjulian CTR5(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER", 2060167385Sjulian __func__, ngq->q_node->nd_ID, ngq->q_node, item ); 2061167385Sjulian 2062167385Sjulian /* Reverse what we did above. That downgrades us back to reader */ 2063167385Sjulian atomic_add_long(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE); 2064177953Smav if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2065177953Smav ng_worklist_add(ngq->q_node); 2066167385Sjulian mtx_unlock_spin(&(ngq->q_mtx)); 2067167385Sjulian 2068167385Sjulian return; 2069167385Sjulian} 2070167385Sjulian 2071167385Sjulian#endif 2072167385Sjulian 207370700Sjulianstatic __inline void 207470700Sjulianng_leave_read(struct ng_queue *ngq) 207570700Sjulian{ 207670700Sjulian atomic_subtract_long(&ngq->q_flags, READER_INCREMENT); 207770700Sjulian} 207870700Sjulian 207970700Sjulianstatic __inline void 208070700Sjulianng_leave_write(struct ng_queue *ngq) 208170700Sjulian{ 2082177953Smav atomic_clear_long(&ngq->q_flags, WRITER_ACTIVE); 208370700Sjulian} 208470700Sjulian 208570700Sjulianstatic void 208670700Sjulianng_flush_input_queue(struct ng_queue * ngq) 208770700Sjulian{ 208870700Sjulian item_p item; 2089151973Sglebius 2090168049Swkoszek NG_QUEUE_LOCK(ngq); 2091151973Sglebius while (ngq->queue) { 209270700Sjulian item = ngq->queue; 209370700Sjulian ngq->queue = item->el_next; 209470700Sjulian if (ngq->last == &(item->el_next)) { 209570700Sjulian ngq->last = &(ngq->queue); 2096177953Smav atomic_clear_long(&ngq->q_flags, OP_PENDING); 209770700Sjulian } 2098168049Swkoszek NG_QUEUE_UNLOCK(ngq); 209970700Sjulian 2100151973Sglebius /* If the item is supplying a callback, call it with an error */ 2101177071Smav if (item->apply != NULL) { 2102177071Smav if (item->depth == 1) 2103177071Smav item->apply->error = ENOENT; 2104177071Smav if (refcount_release(&item->apply->refs)) { 2105177071Smav (*item->apply->apply)(item->apply->context, 2106177071Smav item->apply->error); 2107177071Smav } 2108151283Sglebius } 2109132369Sjulian NG_FREE_ITEM(item); 2110168049Swkoszek NG_QUEUE_LOCK(ngq); 211170700Sjulian } 2112168049Swkoszek NG_QUEUE_UNLOCK(ngq); 211370700Sjulian} 211470700Sjulian 211570700Sjulian/*********************************************************************** 211670700Sjulian* Externally visible method for sending or queueing messages or data. 211770700Sjulian***********************************************************************/ 211870700Sjulian 211970700Sjulian/* 212071849Sjulian * The module code should have filled out the item correctly by this stage: 212170700Sjulian * Common: 212270700Sjulian * reference to destination node. 212370700Sjulian * Reference to destination rcv hook if relevant. 2124172806Smav * apply pointer must be or NULL or reference valid struct ng_apply_info. 212570700Sjulian * Data: 212670700Sjulian * pointer to mbuf 212770700Sjulian * Control_Message: 212870700Sjulian * pointer to msg. 212970700Sjulian * ID of original sender node. (return address) 213071849Sjulian * Function: 213171849Sjulian * Function pointer 213271849Sjulian * void * argument 213371849Sjulian * integer argument 213470700Sjulian * 213570700Sjulian * The nodes have several routines and macros to help with this task: 213670700Sjulian */ 213770700Sjulian 213870700Sjulianint 2139146281Sglebiusng_snd_item(item_p item, int flags) 214070700Sjulian{ 2141172806Smav hook_p hook; 2142172806Smav node_p node; 2143146281Sglebius int queue, rw; 2144172806Smav struct ng_queue *ngq; 2145151256Sglebius int error = 0; 214670700Sjulian 2147176046Smav /* We are sending item, so it must be present! */ 2148176046Smav KASSERT(item != NULL, ("ng_snd_item: item is NULL")); 2149172806Smav 215070784Sjulian#ifdef NETGRAPH_DEBUG 2151152451Sglebius _ngi_check(item, __FILE__, __LINE__); 215270700Sjulian#endif 215370700Sjulian 2154176046Smav /* Item was sent once more, postpone apply() call. */ 2155172806Smav if (item->apply) 2156172806Smav refcount_acquire(&item->apply->refs); 2157146281Sglebius 2158172806Smav node = NGI_NODE(item); 2159176046Smav /* Node is never optional. */ 2160176046Smav KASSERT(node != NULL, ("ng_snd_item: node is NULL")); 2161172806Smav 2162175850Smav hook = NGI_HOOK(item); 2163176046Smav /* Valid hook and mbuf are mandatory for data. */ 2164176046Smav if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) { 2165176046Smav KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL")); 2166131123Sjulian if (NGI_M(item) == NULL) 2167172806Smav ERROUT(EINVAL); 216870700Sjulian CHECK_DATA_MBUF(NGI_M(item)); 216969922Sjulian } 2170149505Sglebius 217170700Sjulian /* 2172176046Smav * If the item or the node specifies single threading, force 2173176046Smav * writer semantics. Similarly, the node may say one hook always 2174176046Smav * produces writers. These are overrides. 217570700Sjulian */ 2176176567Smav if (((item->el_flags & NGQF_RW) == NGQF_WRITER) || 2177176046Smav (node->nd_flags & NGF_FORCE_WRITER) || 2178176046Smav (hook && (hook->hk_flags & HK_FORCE_WRITER))) { 2179176046Smav rw = NGQRW_W; 2180176046Smav } else { 2181176046Smav rw = NGQRW_R; 2182176046Smav } 2183149505Sglebius 2184175847Smav /* 2185175847Smav * If sender or receiver requests queued delivery or stack usage 2186175847Smav * level is dangerous - enqueue message. 2187175847Smav */ 2188175847Smav if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) { 2189175847Smav queue = 1; 2190176046Smav } else { 2191176046Smav queue = 0; 2192175847Smav#ifdef GET_STACK_USAGE 2193175868Smav /* 2194175871Smarck * Most of netgraph nodes have small stack consumption and 2195176046Smav * for them 25% of free stack space is more than enough. 2196175868Smav * Nodes/hooks with higher stack usage should be marked as 2197175889Smarck * HI_STACK. For them 50% of stack will be guaranteed then. 2198176046Smav * XXX: Values 25% and 50% are completely empirical. 2199175868Smav */ 2200176046Smav size_t st, su, sl; 2201175847Smav GET_STACK_USAGE(st, su); 2202176046Smav sl = st - su; 2203176046Smav if ((sl * 4 < st) || 2204176046Smav ((sl * 2 < st) && ((node->nd_flags & NGF_HI_STACK) || 2205175847Smav (hook && (hook->hk_flags & HK_HI_STACK))))) { 2206175847Smav queue = 1; 2207175847Smav } 2208176046Smav#endif 2209175847Smav } 2210175847Smav 2211175850Smav ngq = &node->nd_input_queue; 221270700Sjulian if (queue) { 2213177953Smav item->depth = 1; 221470700Sjulian /* Put it on the queue for that node*/ 221570700Sjulian ng_queue_rw(ngq, item, rw); 2216176046Smav return ((flags & NG_PROGRESS) ? EINPROGRESS : 0); 221770700Sjulian } 221869922Sjulian 221970700Sjulian /* 222070700Sjulian * We already decided how we will be queueud or treated. 222170700Sjulian * Try get the appropriate operating permission. 222270700Sjulian */ 2223151256Sglebius if (rw == NGQRW_R) 222470700Sjulian item = ng_acquire_read(ngq, item); 2225151256Sglebius else 222670700Sjulian item = ng_acquire_write(ngq, item); 222752419Sjulian 2228176046Smav /* Item was queued while trying to get permission. */ 2229176046Smav if (item == NULL) 2230176046Smav return ((flags & NG_PROGRESS) ? EINPROGRESS : 0); 223152419Sjulian 223274078Sjulian NGI_GET_NODE(item, node); /* zaps stored node */ 223352419Sjulian 2234177071Smav item->depth++; 2235170180Sglebius error = ng_apply_item(node, item, rw); /* drops r/w lock when done */ 223674078Sjulian 2237177953Smav /* If something is waiting on queue and ready, schedule it. */ 2238177953Smav if (QUEUE_ACTIVE(ngq)) { 2239177953Smav NG_QUEUE_LOCK(ngq); 2240177953Smav if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2241177953Smav ng_worklist_add(ngq->q_node); 2242177953Smav NG_QUEUE_UNLOCK(ngq); 2243177953Smav } 2244177953Smav 224552419Sjulian /* 2246177953Smav * Node may go away as soon as we remove the reference. 2247177953Smav * Whatever we do, DO NOT access the node again! 224874078Sjulian */ 2249177953Smav NG_NODE_UNREF(node); 225074078Sjulian 225171849Sjulian return (error); 2252172806Smav 2253172806Smavdone: 2254176046Smav /* If was not sent, apply callback here. */ 2255177071Smav if (item->apply != NULL) { 2256177071Smav if (item->depth == 0 && error != 0) 2257177071Smav item->apply->error = error; 2258177071Smav if (refcount_release(&item->apply->refs)) { 2259177071Smav (*item->apply->apply)(item->apply->context, 2260177071Smav item->apply->error); 2261177071Smav } 2262177071Smav } 2263175850Smav 2264172806Smav NG_FREE_ITEM(item); 2265172806Smav return (error); 226652419Sjulian} 226752419Sjulian 226852419Sjulian/* 226970700Sjulian * We have an item that was possibly queued somewhere. 227070700Sjulian * It should contain all the information needed 227170700Sjulian * to run it on the appropriate node/hook. 2272172806Smav * If there is apply pointer and we own the last reference, call apply(). 227352419Sjulian */ 2274170180Sglebiusstatic int 2275151238Sglebiusng_apply_item(node_p node, item_p item, int rw) 227652419Sjulian{ 227770700Sjulian hook_p hook; 227870700Sjulian ng_rcvdata_t *rcvdata; 227971885Sjulian ng_rcvmsg_t *rcvmsg; 2280172806Smav struct ng_apply_info *apply; 2281177071Smav int error = 0, depth; 228252419Sjulian 2283176046Smav /* Node and item are never optional. */ 2284176046Smav KASSERT(node != NULL, ("ng_apply_item: node is NULL")); 2285176046Smav KASSERT(item != NULL, ("ng_apply_item: item is NULL")); 2286176046Smav 228771849Sjulian NGI_GET_HOOK(item, hook); /* clears stored hook */ 228870784Sjulian#ifdef NETGRAPH_DEBUG 2289152451Sglebius _ngi_check(item, __FILE__, __LINE__); 229070700Sjulian#endif 2291147774Sglebius 2292172806Smav apply = item->apply; 2293177071Smav depth = item->depth; 2294147774Sglebius 229571047Sjulian switch (item->el_flags & NGQF_TYPE) { 229670700Sjulian case NGQF_DATA: 229770700Sjulian /* 229870700Sjulian * Check things are still ok as when we were queued. 229970700Sjulian */ 2300176046Smav KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL")); 2301176046Smav if (NG_HOOK_NOT_VALID(hook) || 2302176046Smav NG_NODE_NOT_VALID(node)) { 2303167402Sjulian error = EIO; 230470700Sjulian NG_FREE_ITEM(item); 230571885Sjulian break; 230670700Sjulian } 230771885Sjulian /* 230871885Sjulian * If no receive method, just silently drop it. 230971885Sjulian * Give preference to the hook over-ride method 231071885Sjulian */ 2311152451Sglebius if ((!(rcvdata = hook->hk_rcvdata)) 231271885Sjulian && (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) { 231371885Sjulian error = 0; 231471885Sjulian NG_FREE_ITEM(item); 231571885Sjulian break; 231671885Sjulian } 2317167402Sjulian error = (*rcvdata)(hook, item); 231870700Sjulian break; 231970700Sjulian case NGQF_MESG: 2320175850Smav if (hook && NG_HOOK_NOT_VALID(hook)) { 2321175850Smav /* 2322175850Smav * The hook has been zapped then we can't use it. 2323175850Smav * Immediately drop its reference. 2324175850Smav * The message may not need it. 2325175850Smav */ 2326175850Smav NG_HOOK_UNREF(hook); 2327175850Smav hook = NULL; 232870700Sjulian } 232970700Sjulian /* 233070700Sjulian * Similarly, if the node is a zombie there is 233170700Sjulian * nothing we can do with it, drop everything. 233270700Sjulian */ 233370784Sjulian if (NG_NODE_NOT_VALID(node)) { 233471047Sjulian TRAP_ERROR(); 2335167402Sjulian error = EINVAL; 233670700Sjulian NG_FREE_ITEM(item); 2337175850Smav break; 233870700Sjulian } 2339175850Smav /* 2340175850Smav * Call the appropriate message handler for the object. 2341175850Smav * It is up to the message handler to free the message. 2342175850Smav * If it's a generic message, handle it generically, 2343175850Smav * otherwise call the type's message handler (if it exists). 2344175850Smav * XXX (race). Remember that a queued message may 2345175850Smav * reference a node or hook that has just been 2346175850Smav * invalidated. It will exist as the queue code 2347175850Smav * is holding a reference, but.. 2348175850Smav */ 2349175850Smav if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) && 2350175850Smav ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) { 2351175850Smav error = ng_generic_msg(node, item, hook); 2352175850Smav break; 2353175850Smav } 2354175850Smav if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) && 2355175850Smav (!(rcvmsg = node->nd_type->rcvmsg))) { 2356175850Smav TRAP_ERROR(); 2357175850Smav error = 0; 2358175850Smav NG_FREE_ITEM(item); 2359175850Smav break; 2360175850Smav } 2361175850Smav error = (*rcvmsg)(node, item, hook); 236270700Sjulian break; 236371047Sjulian case NGQF_FN: 2364173605Sglebius case NGQF_FN2: 236571047Sjulian /* 236671047Sjulian * We have to implicitly trust the hook, 236771047Sjulian * as some of these are used for system purposes 236871849Sjulian * where the hook is invalid. In the case of 236971849Sjulian * the shutdown message we allow it to hit 237071849Sjulian * even if the node is invalid. 237171047Sjulian */ 2372152451Sglebius if ((NG_NODE_NOT_VALID(node)) 237371849Sjulian && (NGI_FN(item) != &ng_rmnode)) { 237471047Sjulian TRAP_ERROR(); 2375167402Sjulian error = EINVAL; 2376143384Sglebius NG_FREE_ITEM(item); 237771047Sjulian break; 237871047Sjulian } 2379173605Sglebius if ((item->el_flags & NGQF_TYPE) == NGQF_FN) { 2380173605Sglebius (*NGI_FN(item))(node, hook, NGI_ARG1(item), 2381173605Sglebius NGI_ARG2(item)); 2382172806Smav NG_FREE_ITEM(item); 2383173605Sglebius } else /* it is NGQF_FN2 */ 2384173605Sglebius error = (*NGI_FN2(item))(node, item, hook); 2385172806Smav break; 238670700Sjulian } 238770700Sjulian /* 238870700Sjulian * We held references on some of the resources 238970700Sjulian * that we took from the item. Now that we have 239070700Sjulian * finished doing everything, drop those references. 239170700Sjulian */ 2392175850Smav if (hook) 239370784Sjulian NG_HOOK_UNREF(hook); 239470700Sjulian 2395176046Smav if (rw == NGQRW_R) 239670784Sjulian ng_leave_read(&node->nd_input_queue); 2397176046Smav else 239870784Sjulian ng_leave_write(&node->nd_input_queue); 2399147774Sglebius 2400147774Sglebius /* Apply callback. */ 2401177071Smav if (apply != NULL) { 2402177071Smav if (depth == 1 && error != 0) 2403177071Smav apply->error = error; 2404177071Smav if (refcount_release(&apply->refs)) 2405177071Smav (*apply->apply)(apply->context, apply->error); 2406177071Smav } 2407147774Sglebius 2408170180Sglebius return (error); 240970700Sjulian} 241070700Sjulian 241170700Sjulian/*********************************************************************** 241270700Sjulian * Implement the 'generic' control messages 241370700Sjulian ***********************************************************************/ 241470700Sjulianstatic int 241570700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook) 241670700Sjulian{ 241770700Sjulian int error = 0; 241870700Sjulian struct ng_mesg *msg; 241970700Sjulian struct ng_mesg *resp = NULL; 242070700Sjulian 242170700Sjulian NGI_GET_MSG(item, msg); 242252419Sjulian if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 242371047Sjulian TRAP_ERROR(); 242470700Sjulian error = EINVAL; 242570700Sjulian goto out; 242652419Sjulian } 242752419Sjulian switch (msg->header.cmd) { 242852419Sjulian case NGM_SHUTDOWN: 242971849Sjulian ng_rmnode(here, NULL, NULL, 0); 243052419Sjulian break; 243152419Sjulian case NGM_MKPEER: 243252419Sjulian { 243352419Sjulian struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 243452419Sjulian 243552419Sjulian if (msg->header.arglen != sizeof(*mkp)) { 243671047Sjulian TRAP_ERROR(); 243770700Sjulian error = EINVAL; 243870700Sjulian break; 243952419Sjulian } 244052419Sjulian mkp->type[sizeof(mkp->type) - 1] = '\0'; 244152419Sjulian mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 244252419Sjulian mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 244352419Sjulian error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 244452419Sjulian break; 244552419Sjulian } 244652419Sjulian case NGM_CONNECT: 244752419Sjulian { 244852419Sjulian struct ngm_connect *const con = 244952419Sjulian (struct ngm_connect *) msg->data; 245052419Sjulian node_p node2; 245152419Sjulian 245252419Sjulian if (msg->header.arglen != sizeof(*con)) { 245371047Sjulian TRAP_ERROR(); 245470700Sjulian error = EINVAL; 245570700Sjulian break; 245652419Sjulian } 245752419Sjulian con->path[sizeof(con->path) - 1] = '\0'; 245852419Sjulian con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 245952419Sjulian con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 246070700Sjulian /* Don't forget we get a reference.. */ 246170700Sjulian error = ng_path2noderef(here, con->path, &node2, NULL); 246252419Sjulian if (error) 246352419Sjulian break; 2464172806Smav error = ng_con_nodes(item, here, con->ourhook, 2465172806Smav node2, con->peerhook); 246670784Sjulian NG_NODE_UNREF(node2); 246752419Sjulian break; 246852419Sjulian } 246952419Sjulian case NGM_NAME: 247052419Sjulian { 247152419Sjulian struct ngm_name *const nam = (struct ngm_name *) msg->data; 247252419Sjulian 247352419Sjulian if (msg->header.arglen != sizeof(*nam)) { 247471047Sjulian TRAP_ERROR(); 247570700Sjulian error = EINVAL; 247670700Sjulian break; 247752419Sjulian } 247852419Sjulian nam->name[sizeof(nam->name) - 1] = '\0'; 247952419Sjulian error = ng_name_node(here, nam->name); 248052419Sjulian break; 248152419Sjulian } 248252419Sjulian case NGM_RMHOOK: 248352419Sjulian { 248452419Sjulian struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 248552419Sjulian hook_p hook; 248652419Sjulian 248752419Sjulian if (msg->header.arglen != sizeof(*rmh)) { 248871047Sjulian TRAP_ERROR(); 248970700Sjulian error = EINVAL; 249070700Sjulian break; 249152419Sjulian } 249252419Sjulian rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 249354096Sarchie if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 249452419Sjulian ng_destroy_hook(hook); 249552419Sjulian break; 249652419Sjulian } 249752419Sjulian case NGM_NODEINFO: 249852419Sjulian { 249952419Sjulian struct nodeinfo *ni; 250052419Sjulian 250170700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT); 250252419Sjulian if (resp == NULL) { 250352419Sjulian error = ENOMEM; 250452419Sjulian break; 250552419Sjulian } 250652419Sjulian 250752419Sjulian /* Fill in node info */ 250870700Sjulian ni = (struct nodeinfo *) resp->data; 250970784Sjulian if (NG_NODE_HAS_NAME(here)) 2510125028Sharti strcpy(ni->name, NG_NODE_NAME(here)); 2511125028Sharti strcpy(ni->type, here->nd_type->name); 251252722Sjulian ni->id = ng_node2ID(here); 251370784Sjulian ni->hooks = here->nd_numhooks; 251452419Sjulian break; 251552419Sjulian } 251652419Sjulian case NGM_LISTHOOKS: 251752419Sjulian { 251870784Sjulian const int nhooks = here->nd_numhooks; 251952419Sjulian struct hooklist *hl; 252052419Sjulian struct nodeinfo *ni; 252152419Sjulian hook_p hook; 252252419Sjulian 252352419Sjulian /* Get response struct */ 252470700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*hl) 252570700Sjulian + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 252652419Sjulian if (resp == NULL) { 252752419Sjulian error = ENOMEM; 252852419Sjulian break; 252952419Sjulian } 253070700Sjulian hl = (struct hooklist *) resp->data; 253152419Sjulian ni = &hl->nodeinfo; 253252419Sjulian 253352419Sjulian /* Fill in node info */ 253470784Sjulian if (NG_NODE_HAS_NAME(here)) 2535125028Sharti strcpy(ni->name, NG_NODE_NAME(here)); 2536125028Sharti strcpy(ni->type, here->nd_type->name); 253752722Sjulian ni->id = ng_node2ID(here); 253852419Sjulian 253952419Sjulian /* Cycle through the linked list of hooks */ 254052419Sjulian ni->hooks = 0; 254170784Sjulian LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) { 254252419Sjulian struct linkinfo *const link = &hl->link[ni->hooks]; 254352419Sjulian 254452419Sjulian if (ni->hooks >= nhooks) { 254552419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 254687599Sobrien __func__, "hooks"); 254752419Sjulian break; 254852419Sjulian } 254970784Sjulian if (NG_HOOK_NOT_VALID(hook)) 255052419Sjulian continue; 2551125028Sharti strcpy(link->ourhook, NG_HOOK_NAME(hook)); 2552125028Sharti strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook)); 255370784Sjulian if (NG_PEER_NODE_NAME(hook)[0] != '\0') 2554125028Sharti strcpy(link->nodeinfo.name, 2555125028Sharti NG_PEER_NODE_NAME(hook)); 2556125028Sharti strcpy(link->nodeinfo.type, 2557125028Sharti NG_PEER_NODE(hook)->nd_type->name); 255870784Sjulian link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook)); 255970784Sjulian link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks; 256052419Sjulian ni->hooks++; 256152419Sjulian } 256252419Sjulian break; 256352419Sjulian } 256452419Sjulian 256552419Sjulian case NGM_LISTNAMES: 256652419Sjulian case NGM_LISTNODES: 256752419Sjulian { 256852419Sjulian const int unnamed = (msg->header.cmd == NGM_LISTNODES); 256952419Sjulian struct namelist *nl; 257052419Sjulian node_p node; 2571176802Smav int num = 0, i; 257252419Sjulian 2573176802Smav mtx_lock(&ng_namehash_mtx); 257452419Sjulian /* Count number of nodes */ 2575176802Smav for (i = 0; i < NG_NAME_HASH_SIZE; i++) { 2576176802Smav LIST_FOREACH(node, &ng_name_hash[i], nd_nodes) { 2577176802Smav if (NG_NODE_IS_VALID(node) && 2578176802Smav (unnamed || NG_NODE_HAS_NAME(node))) { 2579176802Smav num++; 2580176802Smav } 258170912Sjulian } 258252419Sjulian } 2583176802Smav mtx_unlock(&ng_namehash_mtx); 258452419Sjulian 258552419Sjulian /* Get response struct */ 258670700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*nl) 258770700Sjulian + (num * sizeof(struct nodeinfo)), M_NOWAIT); 258852419Sjulian if (resp == NULL) { 258952419Sjulian error = ENOMEM; 259052419Sjulian break; 259152419Sjulian } 259270700Sjulian nl = (struct namelist *) resp->data; 259352419Sjulian 259452419Sjulian /* Cycle through the linked list of nodes */ 259552419Sjulian nl->numnames = 0; 2596176802Smav mtx_lock(&ng_namehash_mtx); 2597176802Smav for (i = 0; i < NG_NAME_HASH_SIZE; i++) { 2598176802Smav LIST_FOREACH(node, &ng_name_hash[i], nd_nodes) { 2599176802Smav struct nodeinfo *const np = 2600176802Smav &nl->nodeinfo[nl->numnames]; 260152419Sjulian 2602176802Smav if (NG_NODE_NOT_VALID(node)) 2603176802Smav continue; 2604176802Smav if (!unnamed && (! NG_NODE_HAS_NAME(node))) 2605176802Smav continue; 2606176802Smav if (nl->numnames >= num) { 2607176802Smav log(LOG_ERR, "%s: number of nodes changed\n", 2608176802Smav __func__); 2609176802Smav break; 2610176802Smav } 2611176802Smav if (NG_NODE_HAS_NAME(node)) 2612176802Smav strcpy(np->name, NG_NODE_NAME(node)); 2613176802Smav strcpy(np->type, node->nd_type->name); 2614176802Smav np->id = ng_node2ID(node); 2615176802Smav np->hooks = node->nd_numhooks; 2616176802Smav nl->numnames++; 261752419Sjulian } 261852419Sjulian } 2619176802Smav mtx_unlock(&ng_namehash_mtx); 262052419Sjulian break; 262152419Sjulian } 262252419Sjulian 262352419Sjulian case NGM_LISTTYPES: 262452419Sjulian { 262552419Sjulian struct typelist *tl; 262652419Sjulian struct ng_type *type; 262752419Sjulian int num = 0; 262852419Sjulian 262972200Sbmilekic mtx_lock(&ng_typelist_mtx); 263052419Sjulian /* Count number of types */ 263170912Sjulian LIST_FOREACH(type, &ng_typelist, types) { 263252419Sjulian num++; 263370912Sjulian } 263472200Sbmilekic mtx_unlock(&ng_typelist_mtx); 263552419Sjulian 263652419Sjulian /* Get response struct */ 263770700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*tl) 263870700Sjulian + (num * sizeof(struct typeinfo)), M_NOWAIT); 263952419Sjulian if (resp == NULL) { 264052419Sjulian error = ENOMEM; 264152419Sjulian break; 264252419Sjulian } 264370700Sjulian tl = (struct typelist *) resp->data; 264452419Sjulian 264552419Sjulian /* Cycle through the linked list of types */ 264652419Sjulian tl->numtypes = 0; 264772200Sbmilekic mtx_lock(&ng_typelist_mtx); 264870700Sjulian LIST_FOREACH(type, &ng_typelist, types) { 264952419Sjulian struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 265052419Sjulian 265152419Sjulian if (tl->numtypes >= num) { 265252419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 265387599Sobrien __func__, "types"); 265452419Sjulian break; 265552419Sjulian } 2656125028Sharti strcpy(tp->type_name, type->name); 265771603Sjulian tp->numnodes = type->refs - 1; /* don't count list */ 265852419Sjulian tl->numtypes++; 265952419Sjulian } 266072200Sbmilekic mtx_unlock(&ng_typelist_mtx); 266152419Sjulian break; 266252419Sjulian } 266352419Sjulian 266453913Sarchie case NGM_BINARY2ASCII: 266553913Sarchie { 266664510Sarchie int bufSize = 20 * 1024; /* XXX hard coded constant */ 266753913Sarchie const struct ng_parse_type *argstype; 266853913Sarchie const struct ng_cmdlist *c; 266970700Sjulian struct ng_mesg *binary, *ascii; 267053913Sarchie 267153913Sarchie /* Data area must contain a valid netgraph message */ 267253913Sarchie binary = (struct ng_mesg *)msg->data; 2673152451Sglebius if (msg->header.arglen < sizeof(struct ng_mesg) || 2674152451Sglebius (msg->header.arglen - sizeof(struct ng_mesg) < 2675152451Sglebius binary->header.arglen)) { 267671047Sjulian TRAP_ERROR(); 267753913Sarchie error = EINVAL; 267853913Sarchie break; 267953913Sarchie } 268053913Sarchie 268170700Sjulian /* Get a response message with lots of room */ 268270700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 268370159Sjulian if (resp == NULL) { 268453913Sarchie error = ENOMEM; 268553913Sarchie break; 268653913Sarchie } 268770700Sjulian ascii = (struct ng_mesg *)resp->data; 268853913Sarchie 268953913Sarchie /* Copy binary message header to response message payload */ 269053913Sarchie bcopy(binary, ascii, sizeof(*binary)); 269153913Sarchie 269253913Sarchie /* Find command by matching typecookie and command number */ 269370784Sjulian for (c = here->nd_type->cmdlist; 269453913Sarchie c != NULL && c->name != NULL; c++) { 269553913Sarchie if (binary->header.typecookie == c->cookie 269653913Sarchie && binary->header.cmd == c->cmd) 269753913Sarchie break; 269853913Sarchie } 269953913Sarchie if (c == NULL || c->name == NULL) { 270053913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 270153913Sarchie if (binary->header.typecookie == c->cookie 270253913Sarchie && binary->header.cmd == c->cmd) 270353913Sarchie break; 270453913Sarchie } 270553913Sarchie if (c->name == NULL) { 270670700Sjulian NG_FREE_MSG(resp); 270753913Sarchie error = ENOSYS; 270853913Sarchie break; 270953913Sarchie } 271053913Sarchie } 271153913Sarchie 271253913Sarchie /* Convert command name to ASCII */ 271353913Sarchie snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 271453913Sarchie "%s", c->name); 271553913Sarchie 271653913Sarchie /* Convert command arguments to ASCII */ 271753913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 271853913Sarchie c->respType : c->mesgType; 271970912Sjulian if (argstype == NULL) { 272053913Sarchie *ascii->data = '\0'; 272170912Sjulian } else { 272253913Sarchie if ((error = ng_unparse(argstype, 272353913Sarchie (u_char *)binary->data, 272453913Sarchie ascii->data, bufSize)) != 0) { 272570700Sjulian NG_FREE_MSG(resp); 272653913Sarchie break; 272753913Sarchie } 272853913Sarchie } 272953913Sarchie 273053913Sarchie /* Return the result as struct ng_mesg plus ASCII string */ 273153913Sarchie bufSize = strlen(ascii->data) + 1; 273253913Sarchie ascii->header.arglen = bufSize; 273370700Sjulian resp->header.arglen = sizeof(*ascii) + bufSize; 273453913Sarchie break; 273553913Sarchie } 273653913Sarchie 273753913Sarchie case NGM_ASCII2BINARY: 273853913Sarchie { 273953913Sarchie int bufSize = 2000; /* XXX hard coded constant */ 274053913Sarchie const struct ng_cmdlist *c; 274153913Sarchie const struct ng_parse_type *argstype; 274270700Sjulian struct ng_mesg *ascii, *binary; 274359178Sarchie int off = 0; 274453913Sarchie 274553913Sarchie /* Data area must contain at least a struct ng_mesg + '\0' */ 274653913Sarchie ascii = (struct ng_mesg *)msg->data; 2747152451Sglebius if ((msg->header.arglen < sizeof(*ascii) + 1) || 2748152451Sglebius (ascii->header.arglen < 1) || 2749152451Sglebius (msg->header.arglen < sizeof(*ascii) + 2750152451Sglebius ascii->header.arglen)) { 275171047Sjulian TRAP_ERROR(); 275253913Sarchie error = EINVAL; 275353913Sarchie break; 275453913Sarchie } 275553913Sarchie ascii->data[ascii->header.arglen - 1] = '\0'; 275653913Sarchie 275770700Sjulian /* Get a response message with lots of room */ 275870700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 275970159Sjulian if (resp == NULL) { 276053913Sarchie error = ENOMEM; 276153913Sarchie break; 276253913Sarchie } 276370700Sjulian binary = (struct ng_mesg *)resp->data; 276453913Sarchie 276553913Sarchie /* Copy ASCII message header to response message payload */ 276653913Sarchie bcopy(ascii, binary, sizeof(*ascii)); 276753913Sarchie 276853913Sarchie /* Find command by matching ASCII command string */ 276970784Sjulian for (c = here->nd_type->cmdlist; 277053913Sarchie c != NULL && c->name != NULL; c++) { 277153913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 277253913Sarchie break; 277353913Sarchie } 277453913Sarchie if (c == NULL || c->name == NULL) { 277553913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 277653913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 277753913Sarchie break; 277853913Sarchie } 277953913Sarchie if (c->name == NULL) { 278070700Sjulian NG_FREE_MSG(resp); 278153913Sarchie error = ENOSYS; 278253913Sarchie break; 278353913Sarchie } 278453913Sarchie } 278553913Sarchie 278653913Sarchie /* Convert command name to binary */ 278753913Sarchie binary->header.cmd = c->cmd; 278853913Sarchie binary->header.typecookie = c->cookie; 278953913Sarchie 279053913Sarchie /* Convert command arguments to binary */ 279153913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 279253913Sarchie c->respType : c->mesgType; 279370912Sjulian if (argstype == NULL) { 279453913Sarchie bufSize = 0; 279570912Sjulian } else { 279653913Sarchie if ((error = ng_parse(argstype, ascii->data, 279753913Sarchie &off, (u_char *)binary->data, &bufSize)) != 0) { 279870700Sjulian NG_FREE_MSG(resp); 279953913Sarchie break; 280053913Sarchie } 280153913Sarchie } 280253913Sarchie 280353913Sarchie /* Return the result */ 280453913Sarchie binary->header.arglen = bufSize; 280570700Sjulian resp->header.arglen = sizeof(*binary) + bufSize; 280653913Sarchie break; 280753913Sarchie } 280853913Sarchie 280962471Sphk case NGM_TEXT_CONFIG: 281052419Sjulian case NGM_TEXT_STATUS: 281152419Sjulian /* 281252419Sjulian * This one is tricky as it passes the command down to the 281352419Sjulian * actual node, even though it is a generic type command. 281470700Sjulian * This means we must assume that the item/msg is already freed 281552419Sjulian * when control passes back to us. 281652419Sjulian */ 281770784Sjulian if (here->nd_type->rcvmsg != NULL) { 281870700Sjulian NGI_MSG(item) = msg; /* put it back as we found it */ 281970784Sjulian return((*here->nd_type->rcvmsg)(here, item, lasthook)); 282052419Sjulian } 282152419Sjulian /* Fall through if rcvmsg not supported */ 282252419Sjulian default: 282371047Sjulian TRAP_ERROR(); 282452419Sjulian error = EINVAL; 282552419Sjulian } 282670700Sjulian /* 282770700Sjulian * Sometimes a generic message may be statically allocated 282870700Sjulian * to avoid problems with allocating when in tight memeory situations. 282970700Sjulian * Don't free it if it is so. 283070700Sjulian * I break them appart here, because erros may cause a free if the item 283170700Sjulian * in which case we'd be doing it twice. 283270700Sjulian * they are kept together above, to simplify freeing. 283370700Sjulian */ 283470700Sjulianout: 283570700Sjulian NG_RESPOND_MSG(error, here, item, resp); 283671849Sjulian if (msg) 283770700Sjulian NG_FREE_MSG(msg); 283852419Sjulian return (error); 283952419Sjulian} 284052419Sjulian 284152419Sjulian/************************************************************************ 2842146213Sglebius Queue element get/free routines 2843146213Sglebius************************************************************************/ 2844146213Sglebius 2845146213Sglebiusuma_zone_t ng_qzone; 2846176849Smavstatic int maxalloc = 4096;/* limit the damage of a leak */ 2847176849Smavstatic int maxdata = 512; /* limit the damage of a DoS */ 2848176849Smavstatic int useddata = 0; 2849146213Sglebius 2850146213SglebiusTUNABLE_INT("net.graph.maxalloc", &maxalloc); 2851146213SglebiusSYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc, 2852146213Sglebius 0, "Maximum number of queue items to allocate"); 2853176849SmavTUNABLE_INT("net.graph.maxdata", &maxdata); 2854176849SmavSYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RW | CTLFLAG_TUN, &maxdata, 2855176849Smav 0, "Maximum number of queue data items to allocate"); 2856146213Sglebius 2857146213Sglebius#ifdef NETGRAPH_DEBUG 2858146213Sglebiusstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist); 2859146213Sglebiusstatic int allocated; /* number of items malloc'd */ 2860146213Sglebius#endif 2861146213Sglebius 2862146213Sglebius/* 2863146213Sglebius * Get a queue entry. 2864146213Sglebius * This is usually called when a packet first enters netgraph. 2865146213Sglebius * By definition, this is usually from an interrupt, or from a user. 2866146213Sglebius * Users are not so important, but try be quick for the times that it's 2867146213Sglebius * an interrupt. 2868146213Sglebius */ 2869146213Sglebiusstatic __inline item_p 2870146281Sglebiusng_getqblk(int flags) 2871146213Sglebius{ 2872146213Sglebius item_p item = NULL; 2873146281Sglebius int wait; 2874146213Sglebius 2875146281Sglebius wait = (flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT; 2876146213Sglebius 2877146281Sglebius item = uma_zalloc(ng_qzone, wait | M_ZERO); 2878146281Sglebius 2879146213Sglebius#ifdef NETGRAPH_DEBUG 2880146213Sglebius if (item) { 2881146213Sglebius mtx_lock(&ngq_mtx); 2882146213Sglebius TAILQ_INSERT_TAIL(&ng_itemlist, item, all); 2883146213Sglebius allocated++; 2884146213Sglebius mtx_unlock(&ngq_mtx); 2885146213Sglebius } 2886146213Sglebius#endif 2887146213Sglebius 2888146213Sglebius return (item); 2889146213Sglebius} 2890146213Sglebius 2891146213Sglebius/* 2892146213Sglebius * Release a queue entry 2893146213Sglebius */ 2894146213Sglebiusvoid 2895146213Sglebiusng_free_item(item_p item) 2896146213Sglebius{ 2897146213Sglebius /* 2898146213Sglebius * The item may hold resources on it's own. We need to free 2899146213Sglebius * these before we can free the item. What they are depends upon 2900146213Sglebius * what kind of item it is. it is important that nodes zero 2901146213Sglebius * out pointers to resources that they remove from the item 2902146213Sglebius * or we release them again here. 2903146213Sglebius */ 2904146213Sglebius switch (item->el_flags & NGQF_TYPE) { 2905146213Sglebius case NGQF_DATA: 2906176849Smav atomic_subtract_int(&useddata, 1); 2907146213Sglebius /* If we have an mbuf still attached.. */ 2908146213Sglebius NG_FREE_M(_NGI_M(item)); 2909146213Sglebius break; 2910146213Sglebius case NGQF_MESG: 2911146213Sglebius _NGI_RETADDR(item) = 0; 2912146213Sglebius NG_FREE_MSG(_NGI_MSG(item)); 2913146213Sglebius break; 2914146213Sglebius case NGQF_FN: 2915172806Smav case NGQF_FN2: 2916146213Sglebius /* nothing to free really, */ 2917146213Sglebius _NGI_FN(item) = NULL; 2918146213Sglebius _NGI_ARG1(item) = NULL; 2919146213Sglebius _NGI_ARG2(item) = 0; 2920146213Sglebius break; 2921146213Sglebius } 2922146213Sglebius /* If we still have a node or hook referenced... */ 2923146213Sglebius _NGI_CLR_NODE(item); 2924146213Sglebius _NGI_CLR_HOOK(item); 2925146213Sglebius 2926146213Sglebius#ifdef NETGRAPH_DEBUG 2927146213Sglebius mtx_lock(&ngq_mtx); 2928146213Sglebius TAILQ_REMOVE(&ng_itemlist, item, all); 2929146213Sglebius allocated--; 2930146213Sglebius mtx_unlock(&ngq_mtx); 2931146213Sglebius#endif 2932146213Sglebius uma_zfree(ng_qzone, item); 2933146213Sglebius} 2934146213Sglebius 2935146213Sglebius/************************************************************************ 293652419Sjulian Module routines 293752419Sjulian************************************************************************/ 293852419Sjulian 293952419Sjulian/* 294052419Sjulian * Handle the loading/unloading of a netgraph node type module 294152419Sjulian */ 294252419Sjulianint 294352419Sjulianng_mod_event(module_t mod, int event, void *data) 294452419Sjulian{ 294552419Sjulian struct ng_type *const type = data; 294652419Sjulian int s, error = 0; 294752419Sjulian 294852419Sjulian switch (event) { 294952419Sjulian case MOD_LOAD: 295052419Sjulian 295152419Sjulian /* Register new netgraph node type */ 295252419Sjulian s = splnet(); 295352419Sjulian if ((error = ng_newtype(type)) != 0) { 295452419Sjulian splx(s); 295552419Sjulian break; 295652419Sjulian } 295752419Sjulian 295852419Sjulian /* Call type specific code */ 295952419Sjulian if (type->mod_event != NULL) 296070700Sjulian if ((error = (*type->mod_event)(mod, event, data))) { 296172200Sbmilekic mtx_lock(&ng_typelist_mtx); 296271603Sjulian type->refs--; /* undo it */ 296352419Sjulian LIST_REMOVE(type, types); 296472200Sbmilekic mtx_unlock(&ng_typelist_mtx); 296570700Sjulian } 296652419Sjulian splx(s); 296752419Sjulian break; 296852419Sjulian 296952419Sjulian case MOD_UNLOAD: 297052419Sjulian s = splnet(); 297171603Sjulian if (type->refs > 1) { /* make sure no nodes exist! */ 297252419Sjulian error = EBUSY; 297371603Sjulian } else { 297471603Sjulian if (type->refs == 0) { 297571603Sjulian /* failed load, nothing to undo */ 297671603Sjulian splx(s); 297771603Sjulian break; 297871603Sjulian } 297952419Sjulian if (type->mod_event != NULL) { /* check with type */ 298052419Sjulian error = (*type->mod_event)(mod, event, data); 298152419Sjulian if (error != 0) { /* type refuses.. */ 298252419Sjulian splx(s); 298352419Sjulian break; 298452419Sjulian } 298552419Sjulian } 298672200Sbmilekic mtx_lock(&ng_typelist_mtx); 298752419Sjulian LIST_REMOVE(type, types); 298872200Sbmilekic mtx_unlock(&ng_typelist_mtx); 298952419Sjulian } 299052419Sjulian splx(s); 299152419Sjulian break; 299252419Sjulian 299352419Sjulian default: 299452419Sjulian if (type->mod_event != NULL) 299552419Sjulian error = (*type->mod_event)(mod, event, data); 299652419Sjulian else 2997132199Sphk error = EOPNOTSUPP; /* XXX ? */ 299852419Sjulian break; 299952419Sjulian } 300052419Sjulian return (error); 300152419Sjulian} 300252419Sjulian 300352419Sjulian/* 300452419Sjulian * Handle loading and unloading for this code. 300552419Sjulian * The only thing we need to link into is the NETISR strucure. 300652419Sjulian */ 300752419Sjulianstatic int 300852419Sjulianngb_mod_event(module_t mod, int event, void *data) 300952419Sjulian{ 3010146212Sglebius int error = 0; 301152419Sjulian 301252419Sjulian switch (event) { 301352419Sjulian case MOD_LOAD: 3014146212Sglebius /* Initialize everything. */ 3015168049Swkoszek NG_WORKLIST_LOCK_INIT(); 3016123278Struckman mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL, 3017123278Struckman MTX_DEF); 3018123278Struckman mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL, 3019123278Struckman MTX_DEF); 3020176802Smav mtx_init(&ng_namehash_mtx, "netgraph namehash mutex", NULL, 3021176802Smav MTX_DEF); 3022151974Sglebius mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL, 3023151974Sglebius MTX_DEF); 3024146212Sglebius#ifdef NETGRAPH_DEBUG 3025176802Smav mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL, 3026176802Smav MTX_DEF); 3027146212Sglebius mtx_init(&ngq_mtx, "netgraph item list mutex", NULL, 3028123278Struckman MTX_DEF); 3029146212Sglebius#endif 3030146212Sglebius ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item), 3031146212Sglebius NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0); 3032146212Sglebius uma_zone_set_max(ng_qzone, maxalloc); 3033141719Sglebius netisr_register(NETISR_NETGRAPH, (netisr_t *)ngintr, NULL, 3034141719Sglebius NETISR_MPSAFE); 303552419Sjulian break; 303652419Sjulian case MOD_UNLOAD: 3037167677Srwatson /* You can't unload it because an interface may be using it. */ 303852419Sjulian error = EBUSY; 303952419Sjulian break; 304052419Sjulian default: 304152419Sjulian error = EOPNOTSUPP; 304252419Sjulian break; 304352419Sjulian } 304452419Sjulian return (error); 304552419Sjulian} 304652419Sjulian 304752419Sjulianstatic moduledata_t netgraph_mod = { 304852419Sjulian "netgraph", 304952419Sjulian ngb_mod_event, 305052419Sjulian (NULL) 305152419Sjulian}; 3052139774SemaxDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE); 305372946SjulianSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family"); 305472946SjulianSYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,""); 305572946SjulianSYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, ""); 305652419Sjulian 305770784Sjulian#ifdef NETGRAPH_DEBUG 305870700Sjulianvoid 305970784Sjuliandumphook (hook_p hook, char *file, int line) 306070784Sjulian{ 306170784Sjulian printf("hook: name %s, %d refs, Last touched:\n", 306270784Sjulian _NG_HOOK_NAME(hook), hook->hk_refs); 306370784Sjulian printf(" Last active @ %s, line %d\n", 306470784Sjulian hook->lastfile, hook->lastline); 306570784Sjulian if (line) { 306670784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 306770784Sjulian } 306870784Sjulian} 306970784Sjulian 307070784Sjulianvoid 307170784Sjuliandumpnode(node_p node, char *file, int line) 307270784Sjulian{ 307370784Sjulian printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n", 307471047Sjulian _NG_NODE_ID(node), node->nd_type->name, 307570784Sjulian node->nd_numhooks, node->nd_flags, 307670784Sjulian node->nd_refs, node->nd_name); 307770784Sjulian printf(" Last active @ %s, line %d\n", 307870784Sjulian node->lastfile, node->lastline); 307970784Sjulian if (line) { 308070784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 308170784Sjulian } 308270784Sjulian} 308370784Sjulian 308470784Sjulianvoid 308570700Sjuliandumpitem(item_p item, char *file, int line) 308670700Sjulian{ 3087146212Sglebius printf(" ACTIVE item, last used at %s, line %d", 3088146212Sglebius item->lastfile, item->lastline); 3089146212Sglebius switch(item->el_flags & NGQF_TYPE) { 3090146212Sglebius case NGQF_DATA: 3091146212Sglebius printf(" - [data]\n"); 3092146212Sglebius break; 3093146212Sglebius case NGQF_MESG: 3094146212Sglebius printf(" - retaddr[%d]:\n", _NGI_RETADDR(item)); 3095146212Sglebius break; 3096146212Sglebius case NGQF_FN: 3097172820Sru printf(" - fn@%p (%p, %p, %p, %d (%x))\n", 3098172820Sru _NGI_FN(item), 3099172820Sru _NGI_NODE(item), 3100172820Sru _NGI_HOOK(item), 3101172820Sru item->body.fn.fn_arg1, 3102172820Sru item->body.fn.fn_arg2, 3103172820Sru item->body.fn.fn_arg2); 3104172820Sru break; 3105172806Smav case NGQF_FN2: 3106173110Smav printf(" - fn2@%p (%p, %p, %p, %d (%x))\n", 3107172820Sru _NGI_FN2(item), 3108149735Sglebius _NGI_NODE(item), 3109149735Sglebius _NGI_HOOK(item), 3110146212Sglebius item->body.fn.fn_arg1, 3111146212Sglebius item->body.fn.fn_arg2, 3112146212Sglebius item->body.fn.fn_arg2); 3113146212Sglebius break; 311452419Sjulian } 311570784Sjulian if (line) { 311670784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 3117149735Sglebius if (_NGI_NODE(item)) { 311870784Sjulian printf("node %p ([%x])\n", 3119149735Sglebius _NGI_NODE(item), ng_node2ID(_NGI_NODE(item))); 312070784Sjulian } 312170784Sjulian } 312270700Sjulian} 312352419Sjulian 312470784Sjulianstatic void 312570784Sjulianng_dumpitems(void) 312670784Sjulian{ 312770784Sjulian item_p item; 312870784Sjulian int i = 1; 312970784Sjulian TAILQ_FOREACH(item, &ng_itemlist, all) { 313070784Sjulian printf("[%d] ", i++); 313170784Sjulian dumpitem(item, NULL, 0); 313270784Sjulian } 313370784Sjulian} 313470784Sjulian 313570784Sjulianstatic void 313670784Sjulianng_dumpnodes(void) 313770784Sjulian{ 313870784Sjulian node_p node; 313970784Sjulian int i = 1; 3140131008Srwatson mtx_lock(&ng_nodelist_mtx); 314170784Sjulian SLIST_FOREACH(node, &ng_allnodes, nd_all) { 314270784Sjulian printf("[%d] ", i++); 314370784Sjulian dumpnode(node, NULL, 0); 314470784Sjulian } 3145131008Srwatson mtx_unlock(&ng_nodelist_mtx); 314670784Sjulian} 314770784Sjulian 314870784Sjulianstatic void 314970784Sjulianng_dumphooks(void) 315070784Sjulian{ 315170784Sjulian hook_p hook; 315270784Sjulian int i = 1; 3153131008Srwatson mtx_lock(&ng_nodelist_mtx); 315470784Sjulian SLIST_FOREACH(hook, &ng_allhooks, hk_all) { 315570784Sjulian printf("[%d] ", i++); 315670784Sjulian dumphook(hook, NULL, 0); 315770784Sjulian } 3158131008Srwatson mtx_unlock(&ng_nodelist_mtx); 315970784Sjulian} 316070784Sjulian 316170700Sjulianstatic int 316270700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS) 316370700Sjulian{ 316470700Sjulian int error; 316570700Sjulian int val; 316670700Sjulian int i; 316770700Sjulian 316870700Sjulian val = allocated; 316970700Sjulian i = 1; 3170170289Sdwmalone error = sysctl_handle_int(oidp, &val, 0, req); 317171047Sjulian if (error != 0 || req->newptr == NULL) 317271047Sjulian return (error); 317371047Sjulian if (val == 42) { 317470784Sjulian ng_dumpitems(); 317570784Sjulian ng_dumpnodes(); 317670784Sjulian ng_dumphooks(); 317770700Sjulian } 317871047Sjulian return (0); 317952419Sjulian} 318052419Sjulian 318171047SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW, 318271047Sjulian 0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items"); 318370784Sjulian#endif /* NETGRAPH_DEBUG */ 318470700Sjulian 318570700Sjulian 318670700Sjulian/*********************************************************************** 318770700Sjulian* Worklist routines 318870700Sjulian**********************************************************************/ 318970700Sjulian/* NETISR thread enters here */ 319052419Sjulian/* 319170700Sjulian * Pick a node off the list of nodes with work, 319270700Sjulian * try get an item to process off it. 319370700Sjulian * If there are no more, remove the node from the list. 319452419Sjulian */ 319570700Sjulianstatic void 319670700Sjulianngintr(void) 319752419Sjulian{ 3198177953Smav for (;;) { 3199177953Smav node_p node; 320052419Sjulian 3201177953Smav /* Get node from the worklist. */ 3202168049Swkoszek NG_WORKLIST_LOCK(); 320370700Sjulian node = TAILQ_FIRST(&ng_worklist); 320470700Sjulian if (!node) { 3205168049Swkoszek NG_WORKLIST_UNLOCK(); 320670700Sjulian break; 320769922Sjulian } 320870784Sjulian TAILQ_REMOVE(&ng_worklist, node, nd_work); 3209168049Swkoszek NG_WORKLIST_UNLOCK(); 3210154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist", 3211154275Sglebius __func__, node->nd_ID, node); 321270700Sjulian /* 321370700Sjulian * We have the node. We also take over the reference 321470700Sjulian * that the list had on it. 321570700Sjulian * Now process as much as you can, until it won't 321670700Sjulian * let you have another item off the queue. 321770700Sjulian * All this time, keep the reference 321870700Sjulian * that lets us be sure that the node still exists. 321970700Sjulian * Let the reference go at the last minute. 322070700Sjulian */ 322170700Sjulian for (;;) { 3222177953Smav item_p item; 3223151238Sglebius int rw; 3224151238Sglebius 3225168049Swkoszek NG_QUEUE_LOCK(&node->nd_input_queue); 3226151238Sglebius item = ng_dequeue(&node->nd_input_queue, &rw); 322770700Sjulian if (item == NULL) { 3228177953Smav atomic_clear_int(&node->nd_flags, NGF_WORKQ); 3229168049Swkoszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 323070700Sjulian break; /* go look for another node */ 323170700Sjulian } else { 3232168049Swkoszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 323374078Sjulian NGI_GET_NODE(item, node); /* zaps stored node */ 3234151238Sglebius ng_apply_item(node, item, rw); 323574078Sjulian NG_NODE_UNREF(node); 323670700Sjulian } 323770700Sjulian } 323873238Sjulian NG_NODE_UNREF(node); 323952419Sjulian } 324070700Sjulian} 324169922Sjulian 324272979Sjulian/* 324372979Sjulian * XXX 324472979Sjulian * It's posible that a debugging NG_NODE_REF may need 324572979Sjulian * to be outside the mutex zone 324672979Sjulian */ 324770700Sjulianstatic void 3248177953Smavng_worklist_add(node_p node) 324970700Sjulian{ 3250148236Sglebius 3251148266Sglebius mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED); 3252148236Sglebius 3253132464Sjulian if ((node->nd_flags & NGF_WORKQ) == 0) { 325469922Sjulian /* 325570700Sjulian * If we are not already on the work queue, 325670700Sjulian * then put us on. 325769922Sjulian */ 3258177953Smav atomic_set_int(&node->nd_flags, NGF_WORKQ); 3259177953Smav NG_NODE_REF(node); /* XXX fafe in mutex? */ 3260168049Swkoszek NG_WORKLIST_LOCK(); 326170784Sjulian TAILQ_INSERT_TAIL(&ng_worklist, node, nd_work); 3262168049Swkoszek NG_WORKLIST_UNLOCK(); 3263177953Smav schednetisr(NETISR_NETGRAPH); 3264154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__, 3265154275Sglebius node->nd_ID, node); 3266177953Smav } else { 3267154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist", 3268154275Sglebius __func__, node->nd_ID, node); 3269177953Smav } 327070700Sjulian} 327170700Sjulian 327270700Sjulian 327370700Sjulian/*********************************************************************** 327470700Sjulian* Externally useable functions to set up a queue item ready for sending 327570700Sjulian***********************************************************************/ 327670700Sjulian 327770784Sjulian#ifdef NETGRAPH_DEBUG 327870784Sjulian#define ITEM_DEBUG_CHECKS \ 327970700Sjulian do { \ 328071849Sjulian if (NGI_NODE(item) ) { \ 328170700Sjulian printf("item already has node"); \ 3282174898Srwatson kdb_enter(KDB_WHY_NETGRAPH, "has node"); \ 328371849Sjulian NGI_CLR_NODE(item); \ 328470700Sjulian } \ 328571849Sjulian if (NGI_HOOK(item) ) { \ 328670700Sjulian printf("item already has hook"); \ 3287174898Srwatson kdb_enter(KDB_WHY_NETGRAPH, "has hook"); \ 328871849Sjulian NGI_CLR_HOOK(item); \ 328970700Sjulian } \ 329070700Sjulian } while (0) 329170700Sjulian#else 329270784Sjulian#define ITEM_DEBUG_CHECKS 329370700Sjulian#endif 329470700Sjulian 329570700Sjulian/* 3296131374Sjulian * Put mbuf into the item. 329770700Sjulian * Hook and node references will be removed when the item is dequeued. 329870700Sjulian * (or equivalent) 329970700Sjulian * (XXX) Unsafe because no reference held by peer on remote node. 330070700Sjulian * remote node might go away in this timescale. 330170700Sjulian * We know the hooks can't go away because that would require getting 330270700Sjulian * a writer item on both nodes and we must have at least a reader 3303151973Sglebius * here to be able to do this. 330470700Sjulian * Note that the hook loaded is the REMOTE hook. 330570700Sjulian * 330670700Sjulian * This is possibly in the critical path for new data. 330770700Sjulian */ 330870700Sjulianitem_p 3309146281Sglebiusng_package_data(struct mbuf *m, int flags) 331070700Sjulian{ 331170700Sjulian item_p item; 331270700Sjulian 3313176849Smav if (atomic_fetchadd_int(&useddata, 1) >= maxdata) { 3314176849Smav atomic_subtract_int(&useddata, 1); 3315176849Smav NG_FREE_M(m); 3316176849Smav return (NULL); 3317176849Smav } 3318146281Sglebius if ((item = ng_getqblk(flags)) == NULL) { 331970700Sjulian NG_FREE_M(m); 332070700Sjulian return (NULL); 332170700Sjulian } 332270784Sjulian ITEM_DEBUG_CHECKS; 3323149505Sglebius item->el_flags = NGQF_DATA | NGQF_READER; 332470700Sjulian NGI_M(item) = m; 332570700Sjulian return (item); 332670700Sjulian} 332770700Sjulian 332870700Sjulian/* 332970700Sjulian * Allocate a queue item and put items into it.. 333070700Sjulian * Evaluate the address as this will be needed to queue it and 333170700Sjulian * to work out what some of the fields should be. 333270700Sjulian * Hook and node references will be removed when the item is dequeued. 333370700Sjulian * (or equivalent) 333470700Sjulian */ 333570700Sjulianitem_p 3336146281Sglebiusng_package_msg(struct ng_mesg *msg, int flags) 333770700Sjulian{ 333870700Sjulian item_p item; 333970700Sjulian 3340146281Sglebius if ((item = ng_getqblk(flags)) == NULL) { 334171849Sjulian NG_FREE_MSG(msg); 334270700Sjulian return (NULL); 334369922Sjulian } 334470784Sjulian ITEM_DEBUG_CHECKS; 3345149505Sglebius /* Messages items count as writers unless explicitly exempted. */ 3346149505Sglebius if (msg->header.cmd & NGM_READONLY) 3347149505Sglebius item->el_flags = NGQF_MESG | NGQF_READER; 3348149505Sglebius else 3349149505Sglebius item->el_flags = NGQF_MESG | NGQF_WRITER; 335070700Sjulian /* 335170700Sjulian * Set the current lasthook into the queue item 335270700Sjulian */ 335370700Sjulian NGI_MSG(item) = msg; 3354102244Sarchie NGI_RETADDR(item) = 0; 335570700Sjulian return (item); 335670700Sjulian} 335769922Sjulian 335870700Sjulian 335970700Sjulian 336071849Sjulian#define SET_RETADDR(item, here, retaddr) \ 336171047Sjulian do { /* Data or fn items don't have retaddrs */ \ 336271047Sjulian if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) { \ 336370700Sjulian if (retaddr) { \ 336470700Sjulian NGI_RETADDR(item) = retaddr; \ 336570700Sjulian } else { \ 336670700Sjulian /* \ 336770700Sjulian * The old return address should be ok. \ 336870700Sjulian * If there isn't one, use the address \ 336970700Sjulian * here. \ 337070700Sjulian */ \ 337170700Sjulian if (NGI_RETADDR(item) == 0) { \ 337270700Sjulian NGI_RETADDR(item) \ 337370700Sjulian = ng_node2ID(here); \ 337470700Sjulian } \ 337570700Sjulian } \ 337670700Sjulian } \ 337770700Sjulian } while (0) 337870700Sjulian 337970700Sjulianint 338070700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr) 338170700Sjulian{ 338271849Sjulian hook_p peer; 338371849Sjulian node_p peernode; 338470784Sjulian ITEM_DEBUG_CHECKS; 338570700Sjulian /* 338670700Sjulian * Quick sanity check.. 338770784Sjulian * Since a hook holds a reference on it's node, once we know 338870784Sjulian * that the peer is still connected (even if invalid,) we know 338970784Sjulian * that the peer node is present, though maybe invalid. 339070700Sjulian */ 339170700Sjulian if ((hook == NULL) 339270784Sjulian || NG_HOOK_NOT_VALID(hook) 339370784Sjulian || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)) 339470784Sjulian || NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) { 339570700Sjulian NG_FREE_ITEM(item); 339671047Sjulian TRAP_ERROR(); 339773083Sjulian return (ENETDOWN); 339852419Sjulian } 339952419Sjulian 340070700Sjulian /* 340170700Sjulian * Transfer our interest to the other (peer) end. 340270700Sjulian */ 340371849Sjulian peer = NG_HOOK_PEER(hook); 340471849Sjulian NG_HOOK_REF(peer); 340571849Sjulian NGI_SET_HOOK(item, peer); 340671849Sjulian peernode = NG_PEER_NODE(hook); 340771849Sjulian NG_NODE_REF(peernode); 340871849Sjulian NGI_SET_NODE(item, peernode); 340979706Sjulian SET_RETADDR(item, here, retaddr); 341070700Sjulian return (0); 341170700Sjulian} 341252419Sjulian 341370700Sjulianint 3414152451Sglebiusng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr) 341570700Sjulian{ 3416152451Sglebius node_p dest = NULL; 341770700Sjulian hook_p hook = NULL; 3418152451Sglebius int error; 341970700Sjulian 342070784Sjulian ITEM_DEBUG_CHECKS; 342170700Sjulian /* 342270700Sjulian * Note that ng_path2noderef increments the reference count 342370700Sjulian * on the node for us if it finds one. So we don't have to. 342470700Sjulian */ 342570700Sjulian error = ng_path2noderef(here, address, &dest, &hook); 342670700Sjulian if (error) { 342770700Sjulian NG_FREE_ITEM(item); 342870784Sjulian return (error); 342952419Sjulian } 343071849Sjulian NGI_SET_NODE(item, dest); 343171849Sjulian if ( hook) { 343270784Sjulian NG_HOOK_REF(hook); /* don't let it go while on the queue */ 343371849Sjulian NGI_SET_HOOK(item, hook); 343471849Sjulian } 343571849Sjulian SET_RETADDR(item, here, retaddr); 343670700Sjulian return (0); 343770700Sjulian} 343852419Sjulian 343970700Sjulianint 344070700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr) 344170700Sjulian{ 344270700Sjulian node_p dest; 344370700Sjulian 344470784Sjulian ITEM_DEBUG_CHECKS; 344570700Sjulian /* 344670700Sjulian * Find the target node. 344770700Sjulian */ 344870700Sjulian dest = ng_ID2noderef(ID); /* GETS REFERENCE! */ 344970700Sjulian if (dest == NULL) { 345070700Sjulian NG_FREE_ITEM(item); 345171047Sjulian TRAP_ERROR(); 345270700Sjulian return(EINVAL); 345370700Sjulian } 345470700Sjulian /* Fill out the contents */ 345571849Sjulian NGI_SET_NODE(item, dest); 345671849Sjulian NGI_CLR_HOOK(item); 345771849Sjulian SET_RETADDR(item, here, retaddr); 345852419Sjulian return (0); 345952419Sjulian} 346052419Sjulian 346152419Sjulian/* 346270700Sjulian * special case to send a message to self (e.g. destroy node) 346370700Sjulian * Possibly indicate an arrival hook too. 346470700Sjulian * Useful for removing that hook :-) 346552419Sjulian */ 346670700Sjulianitem_p 346770700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg) 346852419Sjulian{ 346970700Sjulian item_p item; 347052419Sjulian 347170700Sjulian /* 347270700Sjulian * Find the target node. 347370700Sjulian * If there is a HOOK argument, then use that in preference 347470700Sjulian * to the address. 347570700Sjulian */ 3476146281Sglebius if ((item = ng_getqblk(NG_NOFLAGS)) == NULL) { 347771849Sjulian NG_FREE_MSG(msg); 347870700Sjulian return (NULL); 347952419Sjulian } 348070700Sjulian 348170700Sjulian /* Fill out the contents */ 3482149505Sglebius item->el_flags = NGQF_MESG | NGQF_WRITER; 348370784Sjulian NG_NODE_REF(here); 348471849Sjulian NGI_SET_NODE(item, here); 348571849Sjulian if (hook) { 348670784Sjulian NG_HOOK_REF(hook); 348771849Sjulian NGI_SET_HOOK(item, hook); 348871849Sjulian } 348970700Sjulian NGI_MSG(item) = msg; 349070700Sjulian NGI_RETADDR(item) = ng_node2ID(here); 349170700Sjulian return (item); 349252419Sjulian} 349352419Sjulian 3494172806Smav/* 3495172806Smav * Send ng_item_fn function call to the specified node. 3496172806Smav */ 3497172806Smav 3498146281Sglebiusint 3499173605Sglebiusng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2) 350071047Sjulian{ 350171047Sjulian 3502173605Sglebius return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS); 350371047Sjulian} 350471047Sjulian 3505172806Smavint 3506173605Sglebiusng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2, 3507172806Smav int flags) 3508172806Smav{ 3509172806Smav item_p item; 3510172806Smav 3511172806Smav if ((item = ng_getqblk(flags)) == NULL) { 3512172806Smav return (ENOMEM); 3513172806Smav } 3514173605Sglebius item->el_flags = NGQF_FN | NGQF_WRITER; 3515172806Smav NG_NODE_REF(node); /* and one for the item */ 3516172806Smav NGI_SET_NODE(item, node); 3517172806Smav if (hook) { 3518172806Smav NG_HOOK_REF(hook); 3519172806Smav NGI_SET_HOOK(item, hook); 3520172806Smav } 3521173605Sglebius NGI_FN(item) = fn; 3522172806Smav NGI_ARG1(item) = arg1; 3523172806Smav NGI_ARG2(item) = arg2; 3524172806Smav return(ng_snd_item(item, flags)); 3525172806Smav} 3526172806Smav 3527172806Smav/* 3528173605Sglebius * Send ng_item_fn2 function call to the specified node. 3529173605Sglebius * 3530173605Sglebius * If an optional pitem parameter is supplied, its apply 3531173605Sglebius * callback will be copied to the new item. If also NG_REUSE_ITEM 3532173605Sglebius * flag is set, no new item will be allocated, but pitem will 3533173605Sglebius * be used. 3534172806Smav */ 3535172806Smavint 3536173605Sglebiusng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1, 3537173605Sglebius int arg2, int flags) 3538172806Smav{ 3539172806Smav item_p item; 3540172806Smav 3541173605Sglebius KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0), 3542173605Sglebius ("%s: NG_REUSE_ITEM but no pitem", __func__)); 3543172806Smav 3544173605Sglebius /* 3545173605Sglebius * Allocate a new item if no supplied or 3546173605Sglebius * if we can't use supplied one. 3547173605Sglebius */ 3548173605Sglebius if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) { 3549173605Sglebius if ((item = ng_getqblk(flags)) == NULL) 3550173605Sglebius return (ENOMEM); 3551176849Smav } else { 3552176849Smav if ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA) 3553176849Smav atomic_subtract_int(&useddata, 1); 3554173605Sglebius item = pitem; 3555176849Smav } 3556172806Smav 3557172806Smav item->el_flags = NGQF_FN2 | NGQF_WRITER; 3558172806Smav NG_NODE_REF(node); /* and one for the item */ 3559172806Smav NGI_SET_NODE(item, node); 3560172806Smav if (hook) { 3561172806Smav NG_HOOK_REF(hook); 3562172806Smav NGI_SET_HOOK(item, hook); 3563172806Smav } 3564172806Smav NGI_FN2(item) = fn; 3565172806Smav NGI_ARG1(item) = arg1; 3566172806Smav NGI_ARG2(item) = arg2; 3567173605Sglebius if (pitem != NULL && (flags & NG_REUSE_ITEM) == 0) 3568173605Sglebius item->apply = pitem->apply; 3569172806Smav return(ng_snd_item(item, flags)); 3570172806Smav} 3571172806Smav 3572172806Smav/* 357391711Sjulian * Official timeout routines for Netgraph nodes. 357491711Sjulian */ 357591711Sjulianstatic void 3576140852Sglebiusng_callout_trampoline(void *arg) 357791711Sjulian{ 357891711Sjulian item_p item = arg; 357991711Sjulian 358091711Sjulian ng_snd_item(item, 0); 358191711Sjulian} 358291711Sjulian 358391711Sjulian 3584137138Sglebiusint 3585138268Sglebiusng_callout(struct callout *c, node_p node, hook_p hook, int ticks, 358691711Sjulian ng_item_fn *fn, void * arg1, int arg2) 358791711Sjulian{ 3588149881Sglebius item_p item, oitem; 358991711Sjulian 3590146281Sglebius if ((item = ng_getqblk(NG_NOFLAGS)) == NULL) 3591137138Sglebius return (ENOMEM); 3592137138Sglebius 359391711Sjulian item->el_flags = NGQF_FN | NGQF_WRITER; 359491711Sjulian NG_NODE_REF(node); /* and one for the item */ 359591711Sjulian NGI_SET_NODE(item, node); 359691711Sjulian if (hook) { 359791711Sjulian NG_HOOK_REF(hook); 359891711Sjulian NGI_SET_HOOK(item, hook); 359991711Sjulian } 360091711Sjulian NGI_FN(item) = fn; 360191711Sjulian NGI_ARG1(item) = arg1; 360291711Sjulian NGI_ARG2(item) = arg2; 3603149881Sglebius oitem = c->c_arg; 3604149881Sglebius if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 && 3605149881Sglebius oitem != NULL) 3606149881Sglebius NG_FREE_ITEM(oitem); 3607137138Sglebius return (0); 360891711Sjulian} 360991711Sjulian 361091711Sjulian/* A special modified version of untimeout() */ 3611152451Sglebiusint 3612138268Sglebiusng_uncallout(struct callout *c, node_p node) 361391711Sjulian{ 361491711Sjulian item_p item; 3615137138Sglebius int rval; 3616149357Sglebius 3617149357Sglebius KASSERT(c != NULL, ("ng_uncallout: NULL callout")); 3618149357Sglebius KASSERT(node != NULL, ("ng_uncallout: NULL node")); 3619149357Sglebius 3620137230Sglebius rval = callout_stop(c); 3621137138Sglebius item = c->c_arg; 3622137138Sglebius /* Do an extra check */ 3623140852Sglebius if ((rval > 0) && (c->c_func == &ng_callout_trampoline) && 3624137138Sglebius (NGI_NODE(item) == node)) { 362591711Sjulian /* 362691711Sjulian * We successfully removed it from the queue before it ran 3627152451Sglebius * So now we need to unreference everything that was 362891711Sjulian * given extra references. (NG_FREE_ITEM does this). 362991711Sjulian */ 363091711Sjulian NG_FREE_ITEM(item); 363191711Sjulian } 3632149881Sglebius c->c_arg = NULL; 3633137138Sglebius 3634137138Sglebius return (rval); 363591711Sjulian} 363691711Sjulian 363770700Sjulian/* 363870700Sjulian * Set the address, if none given, give the node here. 363970700Sjulian */ 364070700Sjulianvoid 364170700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr) 364270700Sjulian{ 364370700Sjulian if (retaddr) { 364470700Sjulian NGI_RETADDR(item) = retaddr; 364570700Sjulian } else { 364670700Sjulian /* 364770700Sjulian * The old return address should be ok. 364870700Sjulian * If there isn't one, use the address here. 364970700Sjulian */ 365070700Sjulian NGI_RETADDR(item) = ng_node2ID(here); 365170700Sjulian } 365270700Sjulian} 365352419Sjulian 365470700Sjulian#define TESTING 365570700Sjulian#ifdef TESTING 365670700Sjulian/* just test all the macros */ 365770700Sjulianvoid 365870700Sjulianng_macro_test(item_p item); 365970700Sjulianvoid 366070700Sjulianng_macro_test(item_p item) 366170700Sjulian{ 366270700Sjulian node_p node = NULL; 366370700Sjulian hook_p hook = NULL; 366470700Sjulian struct mbuf *m; 366570700Sjulian struct ng_mesg *msg; 366670700Sjulian ng_ID_t retaddr; 366770700Sjulian int error; 366870700Sjulian 366970700Sjulian NGI_GET_M(item, m); 367070700Sjulian NGI_GET_MSG(item, msg); 367170700Sjulian retaddr = NGI_RETADDR(item); 3672131374Sjulian NG_SEND_DATA(error, hook, m, NULL); 367370700Sjulian NG_SEND_DATA_ONLY(error, hook, m); 367470700Sjulian NG_FWD_NEW_DATA(error, item, hook, m); 367570784Sjulian NG_FWD_ITEM_HOOK(error, item, hook); 367670700Sjulian NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr); 367770700Sjulian NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr); 367870700Sjulian NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr); 367970700Sjulian NG_FWD_MSG_HOOK(error, node, item, hook, retaddr); 368070700Sjulian} 368170700Sjulian#endif /* TESTING */ 368270700Sjulian 3683