ng_base.c revision 229003
1139823Simp/*- 252419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc. 352419Sjulian * All rights reserved. 470700Sjulian * 552419Sjulian * Subject to the following obligations and disclaimer of warranty, use and 652419Sjulian * redistribution of this software, in source or object code forms, with or 752419Sjulian * without modifications are expressly permitted by Whistle Communications; 852419Sjulian * provided, however, that: 952419Sjulian * 1. Any and all reproductions of the source or object code must include the 1052419Sjulian * copyright notice above and the following disclaimer of warranties; and 1152419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle 1252419Sjulian * Communications, Inc. trademarks, including the mark "WHISTLE 1352419Sjulian * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1452419Sjulian * such appears in the above copyright notice or in the software. 1570700Sjulian * 1652419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 1752419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 1852419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 1952419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2052419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 2152419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2252419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 2352419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2452419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 2552419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 2652419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 2752419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 2852419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 2952419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3052419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3152419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3252419Sjulian * OF SUCH DAMAGE. 3352419Sjulian * 3467506Sjulian * Authors: Julian Elischer <julian@freebsd.org> 3567506Sjulian * Archie Cobbs <archie@freebsd.org> 3652419Sjulian * 3752419Sjulian * $FreeBSD: head/sys/netgraph/ng_base.c 229003 2011-12-30 15:41:28Z glebius $ 3852419Sjulian * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $ 3952419Sjulian */ 4052419Sjulian 4152419Sjulian/* 4252419Sjulian * This file implements the base netgraph code. 4352419Sjulian */ 4452419Sjulian 4552419Sjulian#include <sys/param.h> 46139236Sglebius#include <sys/systm.h> 47139235Sglebius#include <sys/ctype.h> 4852419Sjulian#include <sys/errno.h> 49131933Smarcel#include <sys/kdb.h> 5052419Sjulian#include <sys/kernel.h> 51154225Sglebius#include <sys/ktr.h> 52114216Skan#include <sys/limits.h> 5352419Sjulian#include <sys/malloc.h> 54139235Sglebius#include <sys/mbuf.h> 5552419Sjulian#include <sys/queue.h> 5672946Sjulian#include <sys/sysctl.h> 57139235Sglebius#include <sys/syslog.h> 58172806Smav#include <sys/refcount.h> 59175847Smav#include <sys/proc.h> 60186093Smav#include <sys/unistd.h> 61186093Smav#include <sys/kthread.h> 62186093Smav#include <sys/smp.h> 63177953Smav#include <machine/cpu.h> 6452419Sjulian 6552419Sjulian#include <net/netisr.h> 66195699Srwatson#include <net/vnet.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 0, /* numhooks */ 11770935Sjulian NULL, /* private */ 11870935Sjulian 0, /* ID */ 119201145Santoine LIST_HEAD_INITIALIZER(ng_deadnode.nd_hooks), 12070935Sjulian {}, /* all_nodes list entry */ 12170935Sjulian {}, /* id hashtable list entry */ 12270935Sjulian { 0, 123178228Smav 0, 12470935Sjulian {}, /* should never use! (should hang) */ 125178228Smav {}, /* workqueue entry */ 126178228Smav STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue), 12770935Sjulian }, 128178228Smav 1, /* refs */ 129191827Szec NULL, /* vnet */ 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, 142148261Sglebius 0, /* undefined data link type */ 14370935Sjulian &ng_deadhook, /* Peer is self */ 14470935Sjulian &ng_deadnode, /* attached to deadnode */ 14570935Sjulian {}, /* hooks list */ 14671885Sjulian NULL, /* override rcvmsg() */ 14771885Sjulian NULL, /* override rcvdata() */ 148178228Smav 1, /* refs always >= 1 */ 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 */ 161178228Smavstatic STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_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 */ 170215701Sdimstatic VNET_DEFINE(LIST_HEAD(, ng_node), ng_ID_hash[NG_ID_HASH_SIZE]); 171195727Srwatson#define V_ng_ID_hash VNET(ng_ID_hash) 172195699Srwatson 17370700Sjulianstatic struct mtx ng_idhash_mtx; 17471354Sjulian/* Method to find a node.. used twice so do it here */ 17571354Sjulian#define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE)) 17671354Sjulian#define NG_IDHASH_FIND(ID, node) \ 17771354Sjulian do { \ 178131008Srwatson mtx_assert(&ng_idhash_mtx, MA_OWNED); \ 179181803Sbz LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)], \ 18071354Sjulian nd_idnodes) { \ 18171354Sjulian if (NG_NODE_IS_VALID(node) \ 18271354Sjulian && (NG_NODE_ID(node) == ID)) { \ 18371354Sjulian break; \ 18471354Sjulian } \ 18571354Sjulian } \ 18671354Sjulian } while (0) 18752722Sjulian 188215701Sdimstatic VNET_DEFINE(LIST_HEAD(, ng_node), ng_name_hash[NG_NAME_HASH_SIZE]); 189195727Srwatson#define V_ng_name_hash VNET(ng_name_hash) 190195699Srwatson 191176802Smavstatic struct mtx ng_namehash_mtx; 192176802Smav#define NG_NAMEHASH(NAME, HASH) \ 193176802Smav do { \ 194176802Smav u_char h = 0; \ 195176802Smav const u_char *c; \ 196176802Smav for (c = (const u_char*)(NAME); *c; c++)\ 197176802Smav h += *c; \ 198176802Smav (HASH) = h % (NG_NAME_HASH_SIZE); \ 199176802Smav } while (0) 20070700Sjulian 201176802Smav 20252419Sjulian/* Internal functions */ 20352419Sjulianstatic int ng_add_hook(node_p node, const char *name, hook_p * hookp); 20470700Sjulianstatic int ng_generic_msg(node_p here, item_p item, hook_p lasthook); 20552722Sjulianstatic ng_ID_t ng_decodeidname(const char *name); 20652419Sjulianstatic int ngb_mod_event(module_t mod, int event, void *data); 207177953Smavstatic void ng_worklist_add(node_p node); 208186093Smavstatic void ngthread(void *); 209170180Sglebiusstatic int ng_apply_item(node_p node, item_p item, int rw); 210178228Smavstatic void ng_flush_input_queue(node_p node); 21170700Sjulianstatic node_p ng_ID2noderef(ng_ID_t ID); 212172806Smavstatic int ng_con_nodes(item_p item, node_p node, const char *name, 213172806Smav node_p node2, const char *name2); 214172806Smavstatic int ng_con_part2(node_p node, item_p item, hook_p hook); 215172806Smavstatic int ng_con_part3(node_p node, item_p item, hook_p hook); 21671047Sjulianstatic int ng_mkpeer(node_p node, const char *name, 21771047Sjulian const char *name2, char *type); 21852419Sjulian 219152451Sglebius/* Imported, these used to be externally visible, some may go back. */ 22070700Sjulianvoid ng_destroy_hook(hook_p hook); 22170700Sjulianint ng_path2noderef(node_p here, const char *path, 22270700Sjulian node_p *dest, hook_p *lasthook); 22370700Sjulianint ng_make_node(const char *type, node_p *nodepp); 22470700Sjulianint ng_path_parse(char *addr, char **node, char **path, char **hook); 22571849Sjulianvoid ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3); 22670784Sjulianvoid ng_unname(node_p node); 22770700Sjulian 22870700Sjulian 22952419Sjulian/* Our own netgraph malloc type */ 23052419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); 23170700SjulianMALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage"); 232227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", 233227293Sed "netgraph hook structures"); 234227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", 235227293Sed "netgraph node structures"); 236227293Sedstatic MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", 237227293Sed "netgraph item structures"); 23852419Sjulian 23970700Sjulian/* Should not be visible outside this file */ 24070784Sjulian 24170784Sjulian#define _NG_ALLOC_HOOK(hook) \ 242184205Sdes hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO) 24370784Sjulian#define _NG_ALLOC_NODE(node) \ 244184205Sdes node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO) 24570784Sjulian 246168049Swkoszek#define NG_QUEUE_LOCK_INIT(n) \ 247168137Swkoszek mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF) 248168049Swkoszek#define NG_QUEUE_LOCK(n) \ 249168137Swkoszek mtx_lock(&(n)->q_mtx) 250168049Swkoszek#define NG_QUEUE_UNLOCK(n) \ 251168137Swkoszek mtx_unlock(&(n)->q_mtx) 252168049Swkoszek#define NG_WORKLIST_LOCK_INIT() \ 253168137Swkoszek mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF) 254168049Swkoszek#define NG_WORKLIST_LOCK() \ 255168137Swkoszek mtx_lock(&ng_worklist_mtx) 256168049Swkoszek#define NG_WORKLIST_UNLOCK() \ 257168137Swkoszek mtx_unlock(&ng_worklist_mtx) 258186093Smav#define NG_WORKLIST_SLEEP() \ 259186093Smav mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0) 260186093Smav#define NG_WORKLIST_WAKEUP() \ 261186093Smav wakeup_one(&ng_worklist) 262168049Swkoszek 26370784Sjulian#ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ 26470784Sjulian/* 26570784Sjulian * In debug mode: 26670784Sjulian * In an attempt to help track reference count screwups 26770784Sjulian * we do not free objects back to the malloc system, but keep them 26870784Sjulian * in a local cache where we can examine them and keep information safely 26970784Sjulian * after they have been freed. 27070784Sjulian * We use this scheme for nodes and hooks, and to some extent for items. 27170784Sjulian */ 27270784Sjulianstatic __inline hook_p 27370784Sjulianng_alloc_hook(void) 27470784Sjulian{ 27570784Sjulian hook_p hook; 27670784Sjulian SLIST_ENTRY(ng_hook) temp; 27772200Sbmilekic mtx_lock(&ng_nodelist_mtx); 27870784Sjulian hook = LIST_FIRST(&ng_freehooks); 27970784Sjulian if (hook) { 28070784Sjulian LIST_REMOVE(hook, hk_hooks); 28170784Sjulian bcopy(&hook->hk_all, &temp, sizeof(temp)); 28270784Sjulian bzero(hook, sizeof(struct ng_hook)); 28370784Sjulian bcopy(&temp, &hook->hk_all, sizeof(temp)); 28472200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 28570784Sjulian hook->hk_magic = HK_MAGIC; 28670784Sjulian } else { 28772200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 28870784Sjulian _NG_ALLOC_HOOK(hook); 28970784Sjulian if (hook) { 29070784Sjulian hook->hk_magic = HK_MAGIC; 29172200Sbmilekic mtx_lock(&ng_nodelist_mtx); 29270784Sjulian SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all); 29372200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 29470784Sjulian } 29570784Sjulian } 29670784Sjulian return (hook); 29770784Sjulian} 29870784Sjulian 29970784Sjulianstatic __inline node_p 30070784Sjulianng_alloc_node(void) 30170784Sjulian{ 30270784Sjulian node_p node; 30370784Sjulian SLIST_ENTRY(ng_node) temp; 30472200Sbmilekic mtx_lock(&ng_nodelist_mtx); 30570784Sjulian node = LIST_FIRST(&ng_freenodes); 30670784Sjulian if (node) { 30770784Sjulian LIST_REMOVE(node, nd_nodes); 30870784Sjulian bcopy(&node->nd_all, &temp, sizeof(temp)); 30970784Sjulian bzero(node, sizeof(struct ng_node)); 31070784Sjulian bcopy(&temp, &node->nd_all, sizeof(temp)); 31172200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 31270784Sjulian node->nd_magic = ND_MAGIC; 31370784Sjulian } else { 31472200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 31570784Sjulian _NG_ALLOC_NODE(node); 31670784Sjulian if (node) { 31770784Sjulian node->nd_magic = ND_MAGIC; 31872200Sbmilekic mtx_lock(&ng_nodelist_mtx); 31970784Sjulian SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all); 32072200Sbmilekic mtx_unlock(&ng_nodelist_mtx); 32170784Sjulian } 32270784Sjulian } 32370784Sjulian return (node); 32470784Sjulian} 32570784Sjulian 32670784Sjulian#define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0) 32770784Sjulian#define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0) 32870784Sjulian 32970784Sjulian 33070784Sjulian#define NG_FREE_HOOK(hook) \ 33170784Sjulian do { \ 332229003Sglebius mtx_lock(&ng_nodelist_mtx); \ 33370784Sjulian LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks); \ 33470784Sjulian hook->hk_magic = 0; \ 335229003Sglebius mtx_unlock(&ng_nodelist_mtx); \ 33670784Sjulian } while (0) 33770784Sjulian 33870784Sjulian#define NG_FREE_NODE(node) \ 33970784Sjulian do { \ 340229003Sglebius mtx_lock(&ng_nodelist_mtx); \ 34170784Sjulian LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes); \ 34270784Sjulian node->nd_magic = 0; \ 343229003Sglebius mtx_unlock(&ng_nodelist_mtx); \ 34470784Sjulian } while (0) 34570784Sjulian 34670784Sjulian#else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ 34770784Sjulian 34870784Sjulian#define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook) 34970784Sjulian#define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node) 35070784Sjulian 351184205Sdes#define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0) 352184205Sdes#define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0) 35370784Sjulian 35470784Sjulian#endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ 35570784Sjulian 356131933Smarcel/* Set this to kdb_enter("X") to catch all errors as they occur */ 35752419Sjulian#ifndef TRAP_ERROR 35871047Sjulian#define TRAP_ERROR() 35952419Sjulian#endif 36052419Sjulian 361215701Sdimstatic VNET_DEFINE(ng_ID_t, nextID) = 1; 362195727Srwatson#define V_nextID VNET(nextID) 36352722Sjulian 36453403Sarchie#ifdef INVARIANTS 36553403Sarchie#define CHECK_DATA_MBUF(m) do { \ 36653403Sarchie struct mbuf *n; \ 36753403Sarchie int total; \ 36853403Sarchie \ 369113255Sdes M_ASSERTPKTHDR(m); \ 370149818Sglebius for (total = 0, n = (m); n != NULL; n = n->m_next) { \ 37153403Sarchie total += n->m_len; \ 372149818Sglebius if (n->m_nextpkt != NULL) \ 373149818Sglebius panic("%s: m_nextpkt", __func__); \ 374149818Sglebius } \ 375149827Sglebius \ 37653403Sarchie if ((m)->m_pkthdr.len != total) { \ 37753403Sarchie panic("%s: %d != %d", \ 37887599Sobrien __func__, (m)->m_pkthdr.len, total); \ 37953403Sarchie } \ 38053403Sarchie } while (0) 38153403Sarchie#else 38253403Sarchie#define CHECK_DATA_MBUF(m) 38353403Sarchie#endif 38452722Sjulian 385172806Smav#define ERROUT(x) do { error = (x); goto done; } while (0) 38653403Sarchie 38752419Sjulian/************************************************************************ 38853913Sarchie Parse type definitions for generic messages 38953913Sarchie************************************************************************/ 39053913Sarchie 39153913Sarchie/* Handy structure parse type defining macro */ 39253913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ 39397685Sarchiestatic const struct ng_parse_struct_field \ 39497685Sarchie ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args; \ 39553913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = { \ 39653913Sarchie &ng_parse_struct_type, \ 39797685Sarchie &ng_ ## lo ## _type_fields \ 39853913Sarchie} 39953913Sarchie 40053913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); 40153913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); 40253913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); 40353913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); 40453913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); 40553913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); 40653913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); 40753913Sarchie 40853913Sarchie/* Get length of an array when the length is stored as a 32 bit 40972645Sasmodai value immediately preceding the array -- as with struct namelist 41053913Sarchie and struct typelist. */ 41153913Sarchiestatic int 41253913Sarchieng_generic_list_getLength(const struct ng_parse_type *type, 41353913Sarchie const u_char *start, const u_char *buf) 41453913Sarchie{ 41553913Sarchie return *((const u_int32_t *)(buf - 4)); 41653913Sarchie} 41753913Sarchie 41853913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */ 41953913Sarchiestatic int 42053913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type, 42153913Sarchie const u_char *start, const u_char *buf) 42253913Sarchie{ 42353913Sarchie const struct hooklist *hl = (const struct hooklist *)start; 42453913Sarchie 42553913Sarchie return hl->nodeinfo.hooks; 42653913Sarchie} 42753913Sarchie 42853913Sarchie/* Array type for a variable length array of struct namelist */ 42953913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = { 43053913Sarchie &ng_generic_nodeinfo_type, 43153913Sarchie &ng_generic_list_getLength 43253913Sarchie}; 43353913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = { 43453913Sarchie &ng_parse_array_type, 43553913Sarchie &ng_nodeinfoarray_type_info 43653913Sarchie}; 43753913Sarchie 43853913Sarchie/* Array type for a variable length array of struct typelist */ 43953913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = { 44053913Sarchie &ng_generic_typeinfo_type, 44153913Sarchie &ng_generic_list_getLength 44253913Sarchie}; 44353913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = { 44453913Sarchie &ng_parse_array_type, 44553913Sarchie &ng_typeinfoarray_type_info 44653913Sarchie}; 44753913Sarchie 44853913Sarchie/* Array type for array of struct linkinfo in struct hooklist */ 44953913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { 45053913Sarchie &ng_generic_linkinfo_type, 45153913Sarchie &ng_generic_linkinfo_getLength 45253913Sarchie}; 45353913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = { 45453913Sarchie &ng_parse_array_type, 45553913Sarchie &ng_generic_linkinfo_array_type_info 45653913Sarchie}; 45753913Sarchie 45853913SarchieDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); 45953913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, 46053913Sarchie (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); 46153913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, 46253913Sarchie (&ng_generic_nodeinfoarray_type)); 46353913Sarchie 46453913Sarchie/* List of commands and how to convert arguments to/from ASCII */ 46553913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = { 46653913Sarchie { 46753913Sarchie NGM_GENERIC_COOKIE, 46853913Sarchie NGM_SHUTDOWN, 46953913Sarchie "shutdown", 47053913Sarchie NULL, 47153913Sarchie NULL 47253913Sarchie }, 47353913Sarchie { 47453913Sarchie NGM_GENERIC_COOKIE, 47553913Sarchie NGM_MKPEER, 47653913Sarchie "mkpeer", 47753913Sarchie &ng_generic_mkpeer_type, 47853913Sarchie NULL 47953913Sarchie }, 48053913Sarchie { 48153913Sarchie NGM_GENERIC_COOKIE, 48253913Sarchie NGM_CONNECT, 48353913Sarchie "connect", 48453913Sarchie &ng_generic_connect_type, 48553913Sarchie NULL 48653913Sarchie }, 48753913Sarchie { 48853913Sarchie NGM_GENERIC_COOKIE, 48953913Sarchie NGM_NAME, 49053913Sarchie "name", 49153913Sarchie &ng_generic_name_type, 49253913Sarchie NULL 49353913Sarchie }, 49453913Sarchie { 49553913Sarchie NGM_GENERIC_COOKIE, 49653913Sarchie NGM_RMHOOK, 49753913Sarchie "rmhook", 49853913Sarchie &ng_generic_rmhook_type, 49953913Sarchie NULL 50053913Sarchie }, 50153913Sarchie { 50253913Sarchie NGM_GENERIC_COOKIE, 50353913Sarchie NGM_NODEINFO, 50453913Sarchie "nodeinfo", 50553913Sarchie NULL, 50653913Sarchie &ng_generic_nodeinfo_type 50753913Sarchie }, 50853913Sarchie { 50953913Sarchie NGM_GENERIC_COOKIE, 51053913Sarchie NGM_LISTHOOKS, 51153913Sarchie "listhooks", 51253913Sarchie NULL, 51353913Sarchie &ng_generic_hooklist_type 51453913Sarchie }, 51553913Sarchie { 51653913Sarchie NGM_GENERIC_COOKIE, 51753913Sarchie NGM_LISTNAMES, 51853913Sarchie "listnames", 51953913Sarchie NULL, 52053913Sarchie &ng_generic_listnodes_type /* same as NGM_LISTNODES */ 52153913Sarchie }, 52253913Sarchie { 52353913Sarchie NGM_GENERIC_COOKIE, 52453913Sarchie NGM_LISTNODES, 52553913Sarchie "listnodes", 52653913Sarchie NULL, 52753913Sarchie &ng_generic_listnodes_type 52853913Sarchie }, 52953913Sarchie { 53053913Sarchie NGM_GENERIC_COOKIE, 53153913Sarchie NGM_LISTTYPES, 53253913Sarchie "listtypes", 53353913Sarchie NULL, 53453913Sarchie &ng_generic_typeinfo_type 53553913Sarchie }, 53653913Sarchie { 53753913Sarchie NGM_GENERIC_COOKIE, 53862471Sphk NGM_TEXT_CONFIG, 53962471Sphk "textconfig", 54062471Sphk NULL, 54162471Sphk &ng_parse_string_type 54262471Sphk }, 54362471Sphk { 54462471Sphk NGM_GENERIC_COOKIE, 54553913Sarchie NGM_TEXT_STATUS, 54653913Sarchie "textstatus", 54753913Sarchie NULL, 54853913Sarchie &ng_parse_string_type 54953913Sarchie }, 55053913Sarchie { 55153913Sarchie NGM_GENERIC_COOKIE, 55253913Sarchie NGM_ASCII2BINARY, 55353913Sarchie "ascii2binary", 55453913Sarchie &ng_parse_ng_mesg_type, 55553913Sarchie &ng_parse_ng_mesg_type 55653913Sarchie }, 55753913Sarchie { 55853913Sarchie NGM_GENERIC_COOKIE, 55953913Sarchie NGM_BINARY2ASCII, 56053913Sarchie "binary2ascii", 56153913Sarchie &ng_parse_ng_mesg_type, 56253913Sarchie &ng_parse_ng_mesg_type 56353913Sarchie }, 56453913Sarchie { 0 } 56553913Sarchie}; 56653913Sarchie 56753913Sarchie/************************************************************************ 56852419Sjulian Node routines 56952419Sjulian************************************************************************/ 57052419Sjulian 57152419Sjulian/* 57252419Sjulian * Instantiate a node of the requested type 57352419Sjulian */ 57452419Sjulianint 57552419Sjulianng_make_node(const char *typename, node_p *nodepp) 57652419Sjulian{ 57752419Sjulian struct ng_type *type; 57870700Sjulian int error; 57952419Sjulian 58052419Sjulian /* Check that the type makes sense */ 58152419Sjulian if (typename == NULL) { 58271047Sjulian TRAP_ERROR(); 58352419Sjulian return (EINVAL); 58452419Sjulian } 58552419Sjulian 586132705Sglebius /* Locate the node type. If we fail we return. Do not try to load 587132705Sglebius * module. 588132705Sglebius */ 589132705Sglebius if ((type = ng_findtype(typename)) == NULL) 590132705Sglebius return (ENXIO); 59152419Sjulian 59270700Sjulian /* 59370700Sjulian * If we have a constructor, then make the node and 59470700Sjulian * call the constructor to do type specific initialisation. 59570700Sjulian */ 59670700Sjulian if (type->constructor != NULL) { 59770700Sjulian if ((error = ng_make_node_common(type, nodepp)) == 0) { 598220745Sglebius if ((error = ((*type->constructor)(*nodepp))) != 0) { 59970784Sjulian NG_NODE_UNREF(*nodepp); 60070700Sjulian } 60170700Sjulian } 60270700Sjulian } else { 60370700Sjulian /* 60470700Sjulian * Node has no constructor. We cannot ask for one 605167677Srwatson * to be made. It must be brought into existence by 60670935Sjulian * some external agency. The external agency should 60770700Sjulian * call ng_make_node_common() directly to get the 60870700Sjulian * netgraph part initialised. 60970700Sjulian */ 61071047Sjulian TRAP_ERROR(); 61170700Sjulian error = EINVAL; 61270700Sjulian } 61370700Sjulian return (error); 61452419Sjulian} 61552419Sjulian 61652419Sjulian/* 61770700Sjulian * Generic node creation. Called by node initialisation for externally 61870700Sjulian * instantiated nodes (e.g. hardware, sockets, etc ). 61952419Sjulian * The returned node has a reference count of 1. 62052419Sjulian */ 62152419Sjulianint 62252419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp) 62352419Sjulian{ 62452419Sjulian node_p node; 62552419Sjulian 62652419Sjulian /* Require the node type to have been already installed */ 62752419Sjulian if (ng_findtype(type->name) == NULL) { 62871047Sjulian TRAP_ERROR(); 62952419Sjulian return (EINVAL); 63052419Sjulian } 63152419Sjulian 63252419Sjulian /* Make a node and try attach it to the type */ 63370784Sjulian NG_ALLOC_NODE(node); 63452419Sjulian if (node == NULL) { 63571047Sjulian TRAP_ERROR(); 63652419Sjulian return (ENOMEM); 63752419Sjulian } 63870784Sjulian node->nd_type = type; 639193731Szec#ifdef VIMAGE 640193731Szec node->nd_vnet = curvnet; 641193731Szec#endif 64270784Sjulian NG_NODE_REF(node); /* note reference */ 64352419Sjulian type->refs++; 64452419Sjulian 645168049Swkoszek NG_QUEUE_LOCK_INIT(&node->nd_input_queue); 646178228Smav STAILQ_INIT(&node->nd_input_queue.queue); 64770784Sjulian node->nd_input_queue.q_flags = 0; 64852419Sjulian 64952419Sjulian /* Initialize hook list for new node */ 65070784Sjulian LIST_INIT(&node->nd_hooks); 65152419Sjulian 652176802Smav /* Link us into the name hash. */ 653176802Smav mtx_lock(&ng_namehash_mtx); 654181803Sbz LIST_INSERT_HEAD(&V_ng_name_hash[0], node, nd_nodes); 655176802Smav mtx_unlock(&ng_namehash_mtx); 65670700Sjulian 65752722Sjulian /* get an ID and put us in the hash chain */ 65872200Sbmilekic mtx_lock(&ng_idhash_mtx); 65970784Sjulian for (;;) { /* wrap protection, even if silly */ 66070700Sjulian node_p node2 = NULL; 661181887Sjulian node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */ 66271354Sjulian 66370784Sjulian /* Is there a problem with the new number? */ 66471354Sjulian NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */ 66571354Sjulian if ((node->nd_ID != 0) && (node2 == NULL)) { 66670784Sjulian break; 66770700Sjulian } 66870784Sjulian } 669229003Sglebius LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], node, 670229003Sglebius nd_idnodes); 67172200Sbmilekic mtx_unlock(&ng_idhash_mtx); 67252722Sjulian 67352419Sjulian /* Done */ 67452419Sjulian *nodepp = node; 67552419Sjulian return (0); 67652419Sjulian} 67752419Sjulian 67852419Sjulian/* 67952419Sjulian * Forceably start the shutdown process on a node. Either call 680167677Srwatson * its shutdown method, or do the default shutdown if there is 68152419Sjulian * no type-specific method. 68252419Sjulian * 683167677Srwatson * We can only be called from a shutdown message, so we know we have 68470939Sjulian * a writer lock, and therefore exclusive access. It also means 68570939Sjulian * that we should not be on the work queue, but we check anyhow. 68670700Sjulian * 68770700Sjulian * Persistent node types must have a type-specific method which 688167677Srwatson * allocates a new node in which case, this one is irretrievably going away, 68970939Sjulian * or cleans up anything it needs, and just makes the node valid again, 690152451Sglebius * in which case we allow the node to survive. 69170939Sjulian * 692167677Srwatson * XXX We need to think of how to tell a persistent node that we 69370939Sjulian * REALLY need to go away because the hardware has gone or we 69470939Sjulian * are rebooting.... etc. 69552419Sjulian */ 69652419Sjulianvoid 69771849Sjulianng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3) 69852419Sjulian{ 69970939Sjulian hook_p hook; 70070939Sjulian 70152419Sjulian /* Check if it's already shutting down */ 702132464Sjulian if ((node->nd_flags & NGF_CLOSING) != 0) 70352419Sjulian return; 70452419Sjulian 70571849Sjulian if (node == &ng_deadnode) { 70671849Sjulian printf ("shutdown called on deadnode\n"); 70771849Sjulian return; 70871849Sjulian } 70971849Sjulian 71052419Sjulian /* Add an extra reference so it doesn't go away during this */ 71170784Sjulian NG_NODE_REF(node); 71252419Sjulian 71370784Sjulian /* 71470784Sjulian * Mark it invalid so any newcomers know not to try use it 71570784Sjulian * Also add our own mark so we can't recurse 716132464Sjulian * note that NGF_INVALID does not do this as it's also set during 71770784Sjulian * creation 71870784Sjulian */ 719132464Sjulian node->nd_flags |= NGF_INVALID|NGF_CLOSING; 72052419Sjulian 721129836Sjulian /* If node has its pre-shutdown method, then call it first*/ 722129836Sjulian if (node->nd_type && node->nd_type->close) 723129836Sjulian (*node->nd_type->close)(node); 724129836Sjulian 72570939Sjulian /* Notify all remaining connected nodes to disconnect */ 72670939Sjulian while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL) 72770939Sjulian ng_destroy_hook(hook); 72870784Sjulian 72970700Sjulian /* 73070700Sjulian * Drain the input queue forceably. 73170784Sjulian * it has no hooks so what's it going to do, bleed on someone? 73270784Sjulian * Theoretically we came here from a queue entry that was added 73370784Sjulian * Just before the queue was closed, so it should be empty anyway. 73471902Sjulian * Also removes us from worklist if needed. 73570700Sjulian */ 736178228Smav ng_flush_input_queue(node); 73770700Sjulian 73852419Sjulian /* Ask the type if it has anything to do in this case */ 73970784Sjulian if (node->nd_type && node->nd_type->shutdown) { 74070784Sjulian (*node->nd_type->shutdown)(node); 74171849Sjulian if (NG_NODE_IS_VALID(node)) { 74271849Sjulian /* 74371849Sjulian * Well, blow me down if the node code hasn't declared 74471849Sjulian * that it doesn't want to die. 74571849Sjulian * Presumably it is a persistant node. 74671849Sjulian * If we REALLY want it to go away, 74771849Sjulian * e.g. hardware going away, 748132464Sjulian * Our caller should set NGF_REALLY_DIE in nd_flags. 749152451Sglebius */ 750132464Sjulian node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING); 75171849Sjulian NG_NODE_UNREF(node); /* Assume they still have theirs */ 75271849Sjulian return; 75371849Sjulian } 75470700Sjulian } else { /* do the default thing */ 75570784Sjulian NG_NODE_UNREF(node); 75652419Sjulian } 75752419Sjulian 75870784Sjulian ng_unname(node); /* basically a NOP these days */ 75970784Sjulian 76070784Sjulian /* 76170784Sjulian * Remove extra reference, possibly the last 76270784Sjulian * Possible other holders of references may include 76370784Sjulian * timeout callouts, but theoretically the node's supposed to 76470784Sjulian * have cancelled them. Possibly hardware dependencies may 76570784Sjulian * force a driver to 'linger' with a reference. 76670784Sjulian */ 76770784Sjulian NG_NODE_UNREF(node); 76852419Sjulian} 76952419Sjulian 77052419Sjulian/* 77174078Sjulian * Remove a reference to the node, possibly the last. 77274078Sjulian * deadnode always acts as it it were the last. 77352419Sjulian */ 774223754Sglebiusvoid 77570784Sjulianng_unref_node(node_p node) 77652419Sjulian{ 77771047Sjulian 778223754Sglebius if (node == &ng_deadnode) 779223754Sglebius return; 78071047Sjulian 781223754Sglebius if (refcount_release(&node->nd_refs)) { /* we were the last */ 78269519Sjulian 783176802Smav mtx_lock(&ng_namehash_mtx); 78470784Sjulian node->nd_type->refs--; /* XXX maybe should get types lock? */ 78570784Sjulian LIST_REMOVE(node, nd_nodes); 786176802Smav mtx_unlock(&ng_namehash_mtx); 78770700Sjulian 78872200Sbmilekic mtx_lock(&ng_idhash_mtx); 78970784Sjulian LIST_REMOVE(node, nd_idnodes); 79072200Sbmilekic mtx_unlock(&ng_idhash_mtx); 79152419Sjulian 79270791Sjulian mtx_destroy(&node->nd_input_queue.q_mtx); 79370700Sjulian NG_FREE_NODE(node); 79452419Sjulian } 79552419Sjulian} 79652419Sjulian 79752419Sjulian/************************************************************************ 79852722Sjulian Node ID handling 79952722Sjulian************************************************************************/ 80052722Sjulianstatic node_p 80170700Sjulianng_ID2noderef(ng_ID_t ID) 80252722Sjulian{ 80370784Sjulian node_p node; 80472200Sbmilekic mtx_lock(&ng_idhash_mtx); 80571354Sjulian NG_IDHASH_FIND(ID, node); 80670784Sjulian if(node) 80770784Sjulian NG_NODE_REF(node); 80872200Sbmilekic mtx_unlock(&ng_idhash_mtx); 80970784Sjulian return(node); 81052722Sjulian} 81152722Sjulian 81252722Sjulianng_ID_t 81352722Sjulianng_node2ID(node_p node) 81452722Sjulian{ 81570912Sjulian return (node ? NG_NODE_ID(node) : 0); 81652722Sjulian} 81752722Sjulian 81852722Sjulian/************************************************************************ 81952419Sjulian Node name handling 82052419Sjulian************************************************************************/ 82152419Sjulian 82252419Sjulian/* 823229003Sglebius * Assign a node a name. 82452419Sjulian */ 82552419Sjulianint 82652419Sjulianng_name_node(node_p node, const char *name) 82752419Sjulian{ 828176802Smav int i, hash; 82970700Sjulian node_p node2; 83052419Sjulian 83152419Sjulian /* Check the name is valid */ 832125028Sharti for (i = 0; i < NG_NODESIZ; i++) { 83352419Sjulian if (name[i] == '\0' || name[i] == '.' || name[i] == ':') 83452419Sjulian break; 83552419Sjulian } 83652419Sjulian if (i == 0 || name[i] != '\0') { 83771047Sjulian TRAP_ERROR(); 83852419Sjulian return (EINVAL); 83952419Sjulian } 84052722Sjulian if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ 84171047Sjulian TRAP_ERROR(); 84252419Sjulian return (EINVAL); 84352419Sjulian } 84452419Sjulian 84552419Sjulian /* Check the name isn't already being used */ 84670700Sjulian if ((node2 = ng_name2noderef(node, name)) != NULL) { 84770784Sjulian NG_NODE_UNREF(node2); 84871047Sjulian TRAP_ERROR(); 84952419Sjulian return (EADDRINUSE); 85052419Sjulian } 85152419Sjulian 85270700Sjulian /* copy it */ 853125028Sharti strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ); 85452419Sjulian 855176802Smav /* Update name hash. */ 856176802Smav NG_NAMEHASH(name, hash); 857176802Smav mtx_lock(&ng_namehash_mtx); 858176802Smav LIST_REMOVE(node, nd_nodes); 859181803Sbz LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes); 860176802Smav mtx_unlock(&ng_namehash_mtx); 861176802Smav 86252419Sjulian return (0); 86352419Sjulian} 86452419Sjulian 86552419Sjulian/* 86652419Sjulian * Find a node by absolute name. The name should NOT end with ':' 86752419Sjulian * The name "." means "this node" and "[xxx]" means "the node 86852419Sjulian * with ID (ie, at address) xxx". 86952419Sjulian * 87052419Sjulian * Returns the node if found, else NULL. 87170700Sjulian * Eventually should add something faster than a sequential search. 872170035Srwatson * Note it acquires a reference on the node so you can be sure it's still 873170035Srwatson * there. 87452419Sjulian */ 87552419Sjuliannode_p 87670700Sjulianng_name2noderef(node_p here, const char *name) 87752419Sjulian{ 87852722Sjulian node_p node; 87952722Sjulian ng_ID_t temp; 880176802Smav int hash; 88152419Sjulian 88252419Sjulian /* "." means "this node" */ 88370700Sjulian if (strcmp(name, ".") == 0) { 88470784Sjulian NG_NODE_REF(here); 88570700Sjulian return(here); 88670700Sjulian } 88752419Sjulian 88852419Sjulian /* Check for name-by-ID */ 88952722Sjulian if ((temp = ng_decodeidname(name)) != 0) { 89070700Sjulian return (ng_ID2noderef(temp)); 89152419Sjulian } 89252419Sjulian 89352419Sjulian /* Find node by name */ 894176802Smav NG_NAMEHASH(name, hash); 895176802Smav mtx_lock(&ng_namehash_mtx); 896181803Sbz LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes) { 897176802Smav if (NG_NODE_IS_VALID(node) && 898176802Smav (strcmp(NG_NODE_NAME(node), name) == 0)) { 89952419Sjulian break; 90070912Sjulian } 90152419Sjulian } 90270700Sjulian if (node) 90370784Sjulian NG_NODE_REF(node); 904176802Smav mtx_unlock(&ng_namehash_mtx); 90552419Sjulian return (node); 90652419Sjulian} 90752419Sjulian 90852419Sjulian/* 909108533Sschweikh * Decode an ID name, eg. "[f03034de]". Returns 0 if the 91052722Sjulian * string is not valid, otherwise returns the value. 91152419Sjulian */ 91252722Sjulianstatic ng_ID_t 91352419Sjulianng_decodeidname(const char *name) 91452419Sjulian{ 91552816Sarchie const int len = strlen(name); 91653648Sarchie char *eptr; 91752816Sarchie u_long val; 91852419Sjulian 91952816Sarchie /* Check for proper length, brackets, no leading junk */ 920229003Sglebius if ((len < 3) || (name[0] != '[') || (name[len - 1] != ']') || 921229003Sglebius (!isxdigit(name[1]))) 92270912Sjulian return ((ng_ID_t)0); 92352419Sjulian 92452816Sarchie /* Decode number */ 92552816Sarchie val = strtoul(name + 1, &eptr, 16); 926229003Sglebius if ((eptr - name != len - 1) || (val == ULONG_MAX) || (val == 0)) 92753042Sjulian return ((ng_ID_t)0); 928229003Sglebius 929229003Sglebius 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. 93552419Sjulian */ 93652419Sjulianvoid 93752419Sjulianng_unname(node_p node) 93852419Sjulian{ 93952419Sjulian} 94052419Sjulian 94152419Sjulian/************************************************************************ 94252419Sjulian Hook routines 94352419Sjulian Names are not optional. Hooks are always connected, except for a 94470939Sjulian brief moment within these routines. On invalidation or during creation 94570939Sjulian they are connected to the 'dead' hook. 94652419Sjulian************************************************************************/ 94752419Sjulian 94852419Sjulian/* 94952419Sjulian * Remove a hook reference 95052419Sjulian */ 95170784Sjulianvoid 95252419Sjulianng_unref_hook(hook_p hook) 95352419Sjulian{ 95471047Sjulian 955223754Sglebius if (hook == &ng_deadhook) 95671047Sjulian return; 95769519Sjulian 958223754Sglebius if (refcount_release(&hook->hk_refs)) { /* we were the last */ 959177722Smav if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */ 96071047Sjulian _NG_NODE_UNREF((_NG_HOOK_NODE(hook))); 96170700Sjulian NG_FREE_HOOK(hook); 96270700Sjulian } 96352419Sjulian} 96452419Sjulian 96552419Sjulian/* 96652419Sjulian * Add an unconnected hook to a node. Only used internally. 96770939Sjulian * Assumes node is locked. (XXX not yet true ) 96852419Sjulian */ 96952419Sjulianstatic int 97052419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp) 97152419Sjulian{ 97252419Sjulian hook_p hook; 97352419Sjulian int error = 0; 97452419Sjulian 97552419Sjulian /* Check that the given name is good */ 97652419Sjulian if (name == NULL) { 97771047Sjulian TRAP_ERROR(); 97852419Sjulian return (EINVAL); 97952419Sjulian } 98054096Sarchie if (ng_findhook(node, name) != NULL) { 98171047Sjulian TRAP_ERROR(); 98254096Sarchie return (EEXIST); 98352419Sjulian } 98452419Sjulian 98552419Sjulian /* Allocate the hook and link it up */ 98670784Sjulian NG_ALLOC_HOOK(hook); 98752419Sjulian if (hook == NULL) { 98871047Sjulian TRAP_ERROR(); 98952419Sjulian return (ENOMEM); 99052419Sjulian } 99170939Sjulian hook->hk_refs = 1; /* add a reference for us to return */ 99270784Sjulian hook->hk_flags = HK_INVALID; 99370939Sjulian hook->hk_peer = &ng_deadhook; /* start off this way */ 99470784Sjulian hook->hk_node = node; 99570784Sjulian NG_NODE_REF(node); /* each hook counts as a reference */ 99652419Sjulian 99770939Sjulian /* Set hook name */ 998125028Sharti strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ); 99970939Sjulian 100070939Sjulian /* 100170939Sjulian * Check if the node type code has something to say about it 100270939Sjulian * If it fails, the unref of the hook will also unref the node. 100370939Sjulian */ 100470935Sjulian if (node->nd_type->newhook != NULL) { 100570935Sjulian if ((error = (*node->nd_type->newhook)(node, hook, name))) { 100670784Sjulian NG_HOOK_UNREF(hook); /* this frees the hook */ 100770700Sjulian return (error); 100870700Sjulian } 100970935Sjulian } 101052419Sjulian /* 101152419Sjulian * The 'type' agrees so far, so go ahead and link it in. 101252419Sjulian * We'll ask again later when we actually connect the hooks. 101352419Sjulian */ 101470784Sjulian LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 101570784Sjulian node->nd_numhooks++; 101670939Sjulian NG_HOOK_REF(hook); /* one for the node */ 101752419Sjulian 101852419Sjulian if (hookp) 101952419Sjulian *hookp = hook; 102070939Sjulian return (0); 102152419Sjulian} 102252419Sjulian 102352419Sjulian/* 102454096Sarchie * Find a hook 102554096Sarchie * 102654096Sarchie * Node types may supply their own optimized routines for finding 102754096Sarchie * hooks. If none is supplied, we just do a linear search. 102870939Sjulian * XXX Possibly we should add a reference to the hook? 102954096Sarchie */ 103054096Sarchiehook_p 103154096Sarchieng_findhook(node_p node, const char *name) 103254096Sarchie{ 103354096Sarchie hook_p hook; 103454096Sarchie 103570784Sjulian if (node->nd_type->findhook != NULL) 103670784Sjulian return (*node->nd_type->findhook)(node, name); 103770784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 1038229003Sglebius if (NG_HOOK_IS_VALID(hook) && 1039229003Sglebius (strcmp(NG_HOOK_NAME(hook), name) == 0)) 104054096Sarchie return (hook); 104154096Sarchie } 104254096Sarchie return (NULL); 104354096Sarchie} 104454096Sarchie 104554096Sarchie/* 104652419Sjulian * Destroy a hook 104752419Sjulian * 104852419Sjulian * As hooks are always attached, this really destroys two hooks. 104952419Sjulian * The one given, and the one attached to it. Disconnect the hooks 105070939Sjulian * from each other first. We reconnect the peer hook to the 'dead' 105170939Sjulian * hook so that it can still exist after we depart. We then 105270939Sjulian * send the peer its own destroy message. This ensures that we only 1053152451Sglebius * interact with the peer's structures when it is locked processing that 105470939Sjulian * message. We hold a reference to the peer hook so we are guaranteed that 105570939Sjulian * the peer hook and node are still going to exist until 105670939Sjulian * we are finished there as the hook holds a ref on the node. 1057152451Sglebius * We run this same code again on the peer hook, but that time it is already 1058152451Sglebius * attached to the 'dead' hook. 105971047Sjulian * 1060152451Sglebius * This routine is called at all stages of hook creation 106171047Sjulian * on error detection and must be able to handle any such stage. 106252419Sjulian */ 106352419Sjulianvoid 106452419Sjulianng_destroy_hook(hook_p hook) 106552419Sjulian{ 1066151974Sglebius hook_p peer; 1067151974Sglebius node_p node; 106852419Sjulian 106971047Sjulian if (hook == &ng_deadhook) { /* better safe than sorry */ 107071047Sjulian printf("ng_destroy_hook called on deadhook\n"); 107171047Sjulian return; 107271047Sjulian } 1073151974Sglebius 1074151974Sglebius /* 1075151974Sglebius * Protect divorce process with mutex, to avoid races on 1076151974Sglebius * simultaneous disconnect. 1077151974Sglebius */ 1078151974Sglebius mtx_lock(&ng_topo_mtx); 1079151974Sglebius 1080151974Sglebius hook->hk_flags |= HK_INVALID; 1081151974Sglebius 1082151974Sglebius peer = NG_HOOK_PEER(hook); 1083151974Sglebius node = NG_HOOK_NODE(hook); 1084151974Sglebius 108570939Sjulian if (peer && (peer != &ng_deadhook)) { 108670939Sjulian /* 108770939Sjulian * Set the peer to point to ng_deadhook 108870939Sjulian * from this moment on we are effectively independent it. 108970939Sjulian * send it an rmhook message of it's own. 109070939Sjulian */ 109170939Sjulian peer->hk_peer = &ng_deadhook; /* They no longer know us */ 109270939Sjulian hook->hk_peer = &ng_deadhook; /* Nor us, them */ 109371047Sjulian if (NG_HOOK_NODE(peer) == &ng_deadnode) { 1094152451Sglebius /* 109571047Sjulian * If it's already divorced from a node, 109671047Sjulian * just free it. 109771047Sjulian */ 1098151974Sglebius mtx_unlock(&ng_topo_mtx); 109971047Sjulian } else { 1100151974Sglebius mtx_unlock(&ng_topo_mtx); 110171047Sjulian ng_rmhook_self(peer); /* Send it a surprise */ 110271047Sjulian } 110370942Sjulian NG_HOOK_UNREF(peer); /* account for peer link */ 110470942Sjulian NG_HOOK_UNREF(hook); /* account for peer link */ 1105151974Sglebius } else 1106151974Sglebius mtx_unlock(&ng_topo_mtx); 110752419Sjulian 1108151974Sglebius mtx_assert(&ng_topo_mtx, MA_NOTOWNED); 1109151974Sglebius 111052419Sjulian /* 111152419Sjulian * Remove the hook from the node's list to avoid possible recursion 111252419Sjulian * in case the disconnection results in node shutdown. 111352419Sjulian */ 111471047Sjulian if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */ 111571047Sjulian return; 111671047Sjulian } 111770784Sjulian LIST_REMOVE(hook, hk_hooks); 111870784Sjulian node->nd_numhooks--; 111970784Sjulian if (node->nd_type->disconnect) { 112052419Sjulian /* 112171047Sjulian * The type handler may elect to destroy the node so don't 1122167677Srwatson * trust its existence after this point. (except 112371047Sjulian * that we still hold a reference on it. (which we 112471047Sjulian * inherrited from the hook we are destroying) 112552419Sjulian */ 112670784Sjulian (*node->nd_type->disconnect) (hook); 112752419Sjulian } 112871047Sjulian 112971047Sjulian /* 113071047Sjulian * Note that because we will point to ng_deadnode, the original node 113171047Sjulian * is not decremented automatically so we do that manually. 113271047Sjulian */ 113371047Sjulian _NG_HOOK_NODE(hook) = &ng_deadnode; 113471047Sjulian NG_NODE_UNREF(node); /* We no longer point to it so adjust count */ 113571047Sjulian NG_HOOK_UNREF(hook); /* Account for linkage (in list) to node */ 113652419Sjulian} 113752419Sjulian 113852419Sjulian/* 113952419Sjulian * Take two hooks on a node and merge the connection so that the given node 114052419Sjulian * is effectively bypassed. 114152419Sjulian */ 114252419Sjulianint 114352419Sjulianng_bypass(hook_p hook1, hook_p hook2) 114452419Sjulian{ 114570784Sjulian if (hook1->hk_node != hook2->hk_node) { 114671047Sjulian TRAP_ERROR(); 114752419Sjulian return (EINVAL); 114870784Sjulian } 1149219827Sglebius mtx_lock(&ng_topo_mtx); 115070784Sjulian hook1->hk_peer->hk_peer = hook2->hk_peer; 115170784Sjulian hook2->hk_peer->hk_peer = hook1->hk_peer; 115252419Sjulian 115370939Sjulian hook1->hk_peer = &ng_deadhook; 115470939Sjulian hook2->hk_peer = &ng_deadhook; 1155219827Sglebius mtx_unlock(&ng_topo_mtx); 115670939Sjulian 1157163244Sglebius NG_HOOK_UNREF(hook1); 1158163244Sglebius NG_HOOK_UNREF(hook2); 1159163244Sglebius 116052419Sjulian /* XXX If we ever cache methods on hooks update them as well */ 116152419Sjulian ng_destroy_hook(hook1); 116252419Sjulian ng_destroy_hook(hook2); 116352419Sjulian return (0); 116452419Sjulian} 116552419Sjulian 116652419Sjulian/* 116752419Sjulian * Install a new netgraph type 116852419Sjulian */ 116952419Sjulianint 117052419Sjulianng_newtype(struct ng_type *tp) 117152419Sjulian{ 117252419Sjulian const size_t namelen = strlen(tp->name); 117352419Sjulian 117452419Sjulian /* Check version and type name fields */ 1175229003Sglebius if ((tp->version != NG_ABI_VERSION) || (namelen == 0) || 1176229003Sglebius (namelen >= NG_TYPESIZ)) { 117771047Sjulian TRAP_ERROR(); 1178131374Sjulian if (tp->version != NG_ABI_VERSION) { 1179229003Sglebius printf("Netgraph: Node type rejected. ABI mismatch. " 1180229003Sglebius "Suggest recompile\n"); 1181131374Sjulian } 118252419Sjulian return (EINVAL); 118352419Sjulian } 118452419Sjulian 118552419Sjulian /* Check for name collision */ 118652419Sjulian if (ng_findtype(tp->name) != NULL) { 118771047Sjulian TRAP_ERROR(); 118852419Sjulian return (EEXIST); 118952419Sjulian } 119052419Sjulian 119170700Sjulian 119252419Sjulian /* Link in new type */ 119372200Sbmilekic mtx_lock(&ng_typelist_mtx); 119470700Sjulian LIST_INSERT_HEAD(&ng_typelist, tp, types); 119571603Sjulian tp->refs = 1; /* first ref is linked list */ 119672200Sbmilekic mtx_unlock(&ng_typelist_mtx); 119752419Sjulian return (0); 119852419Sjulian} 119952419Sjulian 120052419Sjulian/* 120180222Sjulian * unlink a netgraph type 120280222Sjulian * If no examples exist 120380222Sjulian */ 120480222Sjulianint 120580222Sjulianng_rmtype(struct ng_type *tp) 120680222Sjulian{ 120780222Sjulian /* Check for name collision */ 120880222Sjulian if (tp->refs != 1) { 120980222Sjulian TRAP_ERROR(); 121080222Sjulian return (EBUSY); 121180222Sjulian } 121280222Sjulian 121380222Sjulian /* Unlink type */ 121480222Sjulian mtx_lock(&ng_typelist_mtx); 121580222Sjulian LIST_REMOVE(tp, types); 121680222Sjulian mtx_unlock(&ng_typelist_mtx); 121780222Sjulian return (0); 121880222Sjulian} 121980222Sjulian 122080222Sjulian/* 122152419Sjulian * Look for a type of the name given 122252419Sjulian */ 122352419Sjulianstruct ng_type * 122452419Sjulianng_findtype(const char *typename) 122552419Sjulian{ 122652419Sjulian struct ng_type *type; 122752419Sjulian 122872200Sbmilekic mtx_lock(&ng_typelist_mtx); 122970700Sjulian LIST_FOREACH(type, &ng_typelist, types) { 123052419Sjulian if (strcmp(type->name, typename) == 0) 123152419Sjulian break; 123252419Sjulian } 123372200Sbmilekic mtx_unlock(&ng_typelist_mtx); 123452419Sjulian return (type); 123552419Sjulian} 123652419Sjulian 123752419Sjulian/************************************************************************ 123852419Sjulian Composite routines 123952419Sjulian************************************************************************/ 124052419Sjulian/* 124171047Sjulian * Connect two nodes using the specified hooks, using queued functions. 124252419Sjulian */ 1243172806Smavstatic int 1244172806Smavng_con_part3(node_p node, item_p item, hook_p hook) 124552419Sjulian{ 1246172806Smav int error = 0; 124752419Sjulian 124871047Sjulian /* 124971047Sjulian * When we run, we know that the node 'node' is locked for us. 125071047Sjulian * Our caller has a reference on the hook. 125171047Sjulian * Our caller has a reference on the node. 125271047Sjulian * (In this case our caller is ng_apply_item() ). 125371047Sjulian * The peer hook has a reference on the hook. 125471849Sjulian * We are all set up except for the final call to the node, and 125571849Sjulian * the clearing of the INVALID flag. 125671047Sjulian */ 125771047Sjulian if (NG_HOOK_NODE(hook) == &ng_deadnode) { 125871047Sjulian /* 125971047Sjulian * The node must have been freed again since we last visited 126071047Sjulian * here. ng_destry_hook() has this effect but nothing else does. 126171047Sjulian * We should just release our references and 126271047Sjulian * free anything we can think of. 126371047Sjulian * Since we know it's been destroyed, and it's our caller 126471047Sjulian * that holds the references, just return. 126571047Sjulian */ 1266172806Smav ERROUT(ENOENT); 126752419Sjulian } 126871047Sjulian if (hook->hk_node->nd_type->connect) { 1269172806Smav if ((error = (*hook->hk_node->nd_type->connect) (hook))) { 127071047Sjulian ng_destroy_hook(hook); /* also zaps peer */ 127171849Sjulian printf("failed in ng_con_part3()\n"); 1272172806Smav ERROUT(error); 127371047Sjulian } 127452419Sjulian } 127571047Sjulian /* 127671047Sjulian * XXX this is wrong for SMP. Possibly we need 127771047Sjulian * to separate out 'create' and 'invalid' flags. 127871047Sjulian * should only set flags on hooks we have locked under our node. 127971047Sjulian */ 128071047Sjulian hook->hk_flags &= ~HK_INVALID; 1281172806Smavdone: 1282172806Smav NG_FREE_ITEM(item); 1283172806Smav return (error); 128471047Sjulian} 128552419Sjulian 1286172806Smavstatic int 1287172806Smavng_con_part2(node_p node, item_p item, hook_p hook) 128871047Sjulian{ 1289172806Smav hook_p peer; 1290172806Smav int error = 0; 129171047Sjulian 129252419Sjulian /* 129371047Sjulian * When we run, we know that the node 'node' is locked for us. 129471047Sjulian * Our caller has a reference on the hook. 129571047Sjulian * Our caller has a reference on the node. 129671047Sjulian * (In this case our caller is ng_apply_item() ). 129771047Sjulian * The peer hook has a reference on the hook. 129871047Sjulian * our node pointer points to the 'dead' node. 129971047Sjulian * First check the hook name is unique. 130071849Sjulian * Should not happen because we checked before queueing this. 130152419Sjulian */ 130271047Sjulian if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) { 130371047Sjulian TRAP_ERROR(); 130471047Sjulian ng_destroy_hook(hook); /* should destroy peer too */ 130571849Sjulian printf("failed in ng_con_part2()\n"); 1306172806Smav ERROUT(EEXIST); 130771047Sjulian } 130870939Sjulian /* 130971047Sjulian * Check if the node type code has something to say about it 131071047Sjulian * If it fails, the unref of the hook will also unref the attached node, 131171047Sjulian * however since that node is 'ng_deadnode' this will do nothing. 131271047Sjulian * The peer hook will also be destroyed. 131370939Sjulian */ 131471047Sjulian if (node->nd_type->newhook != NULL) { 1315172806Smav if ((error = (*node->nd_type->newhook)(node, hook, 1316172806Smav hook->hk_name))) { 131771047Sjulian ng_destroy_hook(hook); /* should destroy peer too */ 131871849Sjulian printf("failed in ng_con_part2()\n"); 1319172806Smav ERROUT(error); 132071047Sjulian } 132171047Sjulian } 132271047Sjulian 132371047Sjulian /* 132471047Sjulian * The 'type' agrees so far, so go ahead and link it in. 132571047Sjulian * We'll ask again later when we actually connect the hooks. 132671047Sjulian */ 132771047Sjulian hook->hk_node = node; /* just overwrite ng_deadnode */ 132871047Sjulian NG_NODE_REF(node); /* each hook counts as a reference */ 132971047Sjulian LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 133071047Sjulian node->nd_numhooks++; 133171047Sjulian NG_HOOK_REF(hook); /* one for the node */ 133271047Sjulian 133371047Sjulian /* 1334167677Srwatson * We now have a symmetrical situation, where both hooks have been 133574078Sjulian * linked to their nodes, the newhook methods have been called 133671047Sjulian * And the references are all correct. The hooks are still marked 133771047Sjulian * as invalid, as we have not called the 'connect' methods 133871047Sjulian * yet. 1339167677Srwatson * We can call the local one immediately as we have the 134071047Sjulian * node locked, but we need to queue the remote one. 134171047Sjulian */ 134271047Sjulian if (hook->hk_node->nd_type->connect) { 1343172806Smav if ((error = (*hook->hk_node->nd_type->connect) (hook))) { 134471047Sjulian ng_destroy_hook(hook); /* also zaps peer */ 134571849Sjulian printf("failed in ng_con_part2(A)\n"); 1346172806Smav ERROUT(error); 134771047Sjulian } 134871047Sjulian } 1349151974Sglebius 1350151974Sglebius /* 1351151974Sglebius * Acquire topo mutex to avoid race with ng_destroy_hook(). 1352151974Sglebius */ 1353151974Sglebius mtx_lock(&ng_topo_mtx); 1354151974Sglebius peer = hook->hk_peer; 1355151974Sglebius if (peer == &ng_deadhook) { 1356151974Sglebius mtx_unlock(&ng_topo_mtx); 1357151974Sglebius printf("failed in ng_con_part2(B)\n"); 1358151974Sglebius ng_destroy_hook(hook); 1359172806Smav ERROUT(ENOENT); 1360151974Sglebius } 1361151974Sglebius mtx_unlock(&ng_topo_mtx); 1362151974Sglebius 1363173605Sglebius if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3, 1364173605Sglebius NULL, 0, NG_REUSE_ITEM))) { 1365151974Sglebius printf("failed in ng_con_part2(C)\n"); 136671849Sjulian ng_destroy_hook(hook); /* also zaps peer */ 1367172806Smav return (error); /* item was consumed. */ 136871849Sjulian } 136971047Sjulian hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */ 1370172806Smav return (0); /* item was consumed. */ 1371172806Smavdone: 1372172806Smav NG_FREE_ITEM(item); 1373172806Smav return (error); 137452419Sjulian} 137552419Sjulian 137652419Sjulian/* 1377152451Sglebius * Connect this node with another node. We assume that this node is 137871047Sjulian * currently locked, as we are only called from an NGM_CONNECT message. 137952419Sjulian */ 138071047Sjulianstatic int 1381172806Smavng_con_nodes(item_p item, node_p node, const char *name, 1382172806Smav node_p node2, const char *name2) 138352419Sjulian{ 1384152451Sglebius int error; 1385152451Sglebius hook_p hook; 1386152451Sglebius hook_p hook2; 138752419Sjulian 138871849Sjulian if (ng_findhook(node2, name2) != NULL) { 138971849Sjulian return(EEXIST); 139071849Sjulian } 139170939Sjulian if ((error = ng_add_hook(node, name, &hook))) /* gives us a ref */ 139252419Sjulian return (error); 139371047Sjulian /* Allocate the other hook and link it up */ 139471047Sjulian NG_ALLOC_HOOK(hook2); 1395140737Sglebius if (hook2 == NULL) { 139671047Sjulian TRAP_ERROR(); 139771047Sjulian ng_destroy_hook(hook); /* XXX check ref counts so far */ 139871047Sjulian NG_HOOK_UNREF(hook); /* including our ref */ 139971047Sjulian return (ENOMEM); 140071047Sjulian } 140171047Sjulian hook2->hk_refs = 1; /* start with a reference for us. */ 140271047Sjulian hook2->hk_flags = HK_INVALID; 140371047Sjulian hook2->hk_peer = hook; /* Link the two together */ 140471047Sjulian hook->hk_peer = hook2; 140571047Sjulian NG_HOOK_REF(hook); /* Add a ref for the peer to each*/ 140671047Sjulian NG_HOOK_REF(hook2); 1407152451Sglebius hook2->hk_node = &ng_deadnode; 1408125028Sharti strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ); 140971047Sjulian 141071047Sjulian /* 141171047Sjulian * Queue the function above. 141271047Sjulian * Procesing continues in that function in the lock context of 141371047Sjulian * the other node. 141471047Sjulian */ 1415173605Sglebius if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0, 1416173605Sglebius NG_NOFLAGS))) { 1417171885Smav printf("failed in ng_con_nodes(): %d\n", error); 1418171885Smav ng_destroy_hook(hook); /* also zaps peer */ 1419171885Smav } 142071047Sjulian 142171047Sjulian NG_HOOK_UNREF(hook); /* Let each hook go if it wants to */ 142271047Sjulian NG_HOOK_UNREF(hook2); 1423171885Smav return (error); 142471047Sjulian} 142571047Sjulian 142671047Sjulian/* 142771047Sjulian * Make a peer and connect. 142871047Sjulian * We assume that the local node is locked. 142971047Sjulian * The new node probably doesn't need a lock until 143071047Sjulian * it has a hook, because it cannot really have any work until then, 143171047Sjulian * but we should think about it a bit more. 143271047Sjulian * 143371047Sjulian * The problem may come if the other node also fires up 143471047Sjulian * some hardware or a timer or some other source of activation, 143571047Sjulian * also it may already get a command msg via it's ID. 143671047Sjulian * 143771047Sjulian * We could use the same method as ng_con_nodes() but we'd have 1438152451Sglebius * to add ability to remove the node when failing. (Not hard, just 143971047Sjulian * make arg1 point to the node to remove). 144071047Sjulian * Unless of course we just ignore failure to connect and leave 144171047Sjulian * an unconnected node? 144271047Sjulian */ 144371047Sjulianstatic int 144471047Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type) 144571047Sjulian{ 1446152451Sglebius node_p node2; 1447152451Sglebius hook_p hook1, hook2; 1448152451Sglebius int error; 144971047Sjulian 145071047Sjulian if ((error = ng_make_node(type, &node2))) { 145152419Sjulian return (error); 145252419Sjulian } 145370939Sjulian 145471047Sjulian if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */ 145571849Sjulian ng_rmnode(node2, NULL, NULL, 0); 145671047Sjulian return (error); 145771047Sjulian } 145871047Sjulian 145971047Sjulian if ((error = ng_add_hook(node2, name2, &hook2))) { 146071849Sjulian ng_rmnode(node2, NULL, NULL, 0); 146171047Sjulian ng_destroy_hook(hook1); 146271047Sjulian NG_HOOK_UNREF(hook1); 146371047Sjulian return (error); 146471047Sjulian } 146571047Sjulian 146670939Sjulian /* 146771047Sjulian * Actually link the two hooks together. 146871047Sjulian */ 146971047Sjulian hook1->hk_peer = hook2; 147071047Sjulian hook2->hk_peer = hook1; 147171047Sjulian 147271047Sjulian /* Each hook is referenced by the other */ 147371047Sjulian NG_HOOK_REF(hook1); 147471047Sjulian NG_HOOK_REF(hook2); 147571047Sjulian 147671047Sjulian /* Give each node the opportunity to veto the pending connection */ 147771047Sjulian if (hook1->hk_node->nd_type->connect) { 147871047Sjulian error = (*hook1->hk_node->nd_type->connect) (hook1); 147971047Sjulian } 148071047Sjulian 148171047Sjulian if ((error == 0) && hook2->hk_node->nd_type->connect) { 148271047Sjulian error = (*hook2->hk_node->nd_type->connect) (hook2); 148371047Sjulian 148471047Sjulian } 148571047Sjulian 148671047Sjulian /* 148770939Sjulian * drop the references we were holding on the two hooks. 148870939Sjulian */ 148971047Sjulian if (error) { 149071047Sjulian ng_destroy_hook(hook2); /* also zaps hook1 */ 149171849Sjulian ng_rmnode(node2, NULL, NULL, 0); 149271047Sjulian } else { 149371047Sjulian /* As a last act, allow the hooks to be used */ 149471047Sjulian hook1->hk_flags &= ~HK_INVALID; 149571047Sjulian hook2->hk_flags &= ~HK_INVALID; 149671047Sjulian } 149771047Sjulian NG_HOOK_UNREF(hook1); 149870939Sjulian NG_HOOK_UNREF(hook2); 149970939Sjulian return (error); 150052419Sjulian} 150171047Sjulian 150270700Sjulian/************************************************************************ 150370700Sjulian Utility routines to send self messages 150470700Sjulian************************************************************************/ 150570700Sjulian 150671849Sjulian/* Shut this node down as soon as everyone is clear of it */ 1507167677Srwatson/* Should add arg "immediately" to jump the queue */ 150870700Sjulianint 1509186060Smavng_rmnode_self(node_p node) 151070700Sjulian{ 151171849Sjulian int error; 151252419Sjulian 151371849Sjulian if (node == &ng_deadnode) 151471849Sjulian return (0); 1515132464Sjulian node->nd_flags |= NGF_INVALID; 1516132464Sjulian if (node->nd_flags & NGF_CLOSING) 151771849Sjulian return (0); 151870700Sjulian 1519186060Smav error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0); 152071849Sjulian return (error); 152170700Sjulian} 152270700Sjulian 152371849Sjulianstatic void 152471047Sjulianng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2) 152571047Sjulian{ 152671047Sjulian ng_destroy_hook(hook); 152771849Sjulian return ; 152871047Sjulian} 152971047Sjulian 153070935Sjulianint 153170935Sjulianng_rmhook_self(hook_p hook) 153270935Sjulian{ 153371047Sjulian int error; 153470935Sjulian node_p node = NG_HOOK_NODE(hook); 153570935Sjulian 153671047Sjulian if (node == &ng_deadnode) 153771047Sjulian return (0); 153871047Sjulian 153971047Sjulian error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0); 154071047Sjulian return (error); 154170935Sjulian} 154270935Sjulian 154370700Sjulian/*********************************************************************** 154452419Sjulian * Parse and verify a string of the form: <NODE:><PATH> 154552419Sjulian * 154652419Sjulian * Such a string can refer to a specific node or a specific hook 154752419Sjulian * on a specific node, depending on how you look at it. In the 154852419Sjulian * latter case, the PATH component must not end in a dot. 154952419Sjulian * 155052419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string 155152419Sjulian * of hook names separated by dots. This breaks out the original 155252419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp 155352419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to 155452419Sjulian * the final hook component of <PATH>, if any, otherwise NULL. 155552419Sjulian * 155652419Sjulian * This returns -1 if the path is malformed. The char ** are optional. 155770700Sjulian ***********************************************************************/ 155852419Sjulianint 155952419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 156052419Sjulian{ 1561152451Sglebius char *node, *path, *hook; 1562152451Sglebius int k; 156352419Sjulian 156452419Sjulian /* 156552419Sjulian * Extract absolute NODE, if any 156652419Sjulian */ 156752419Sjulian for (path = addr; *path && *path != ':'; path++); 156852419Sjulian if (*path) { 156952419Sjulian node = addr; /* Here's the NODE */ 157052419Sjulian *path++ = '\0'; /* Here's the PATH */ 157152419Sjulian 157252419Sjulian /* Node name must not be empty */ 157352419Sjulian if (!*node) 157452419Sjulian return -1; 157552419Sjulian 157652419Sjulian /* A name of "." is OK; otherwise '.' not allowed */ 157752419Sjulian if (strcmp(node, ".") != 0) { 157852419Sjulian for (k = 0; node[k]; k++) 157952419Sjulian if (node[k] == '.') 158052419Sjulian return -1; 158152419Sjulian } 158252419Sjulian } else { 158352419Sjulian node = NULL; /* No absolute NODE */ 158452419Sjulian path = addr; /* Here's the PATH */ 158552419Sjulian } 158652419Sjulian 158752419Sjulian /* Snoop for illegal characters in PATH */ 158852419Sjulian for (k = 0; path[k]; k++) 158952419Sjulian if (path[k] == ':') 159052419Sjulian return -1; 159152419Sjulian 159252419Sjulian /* Check for no repeated dots in PATH */ 159352419Sjulian for (k = 0; path[k]; k++) 159452419Sjulian if (path[k] == '.' && path[k + 1] == '.') 159552419Sjulian return -1; 159652419Sjulian 159752419Sjulian /* Remove extra (degenerate) dots from beginning or end of PATH */ 159852419Sjulian if (path[0] == '.') 159952419Sjulian path++; 160052419Sjulian if (*path && path[strlen(path) - 1] == '.') 160152419Sjulian path[strlen(path) - 1] = 0; 160252419Sjulian 160352419Sjulian /* If PATH has a dot, then we're not talking about a hook */ 160452419Sjulian if (*path) { 160552419Sjulian for (hook = path, k = 0; path[k]; k++) 160652419Sjulian if (path[k] == '.') { 160752419Sjulian hook = NULL; 160852419Sjulian break; 160952419Sjulian } 161052419Sjulian } else 161152419Sjulian path = hook = NULL; 161252419Sjulian 161352419Sjulian /* Done */ 161452419Sjulian if (nodep) 161552419Sjulian *nodep = node; 161652419Sjulian if (pathp) 161752419Sjulian *pathp = path; 161852419Sjulian if (hookp) 161952419Sjulian *hookp = hook; 162052419Sjulian return (0); 162152419Sjulian} 162252419Sjulian 162352419Sjulian/* 162452419Sjulian * Given a path, which may be absolute or relative, and a starting node, 162570700Sjulian * return the destination node. 162652419Sjulian */ 162752419Sjulianint 1628229003Sglebiusng_path2noderef(node_p here, const char *address, node_p *destp, 1629229003Sglebius hook_p *lasthook) 163052419Sjulian{ 1631125028Sharti char fullpath[NG_PATHSIZ]; 1632219827Sglebius char *nodename, *path; 163370700Sjulian node_p node, oldnode; 163452419Sjulian 163552419Sjulian /* Initialize */ 163670784Sjulian if (destp == NULL) { 163771047Sjulian TRAP_ERROR(); 163852419Sjulian return EINVAL; 163970784Sjulian } 164052419Sjulian *destp = NULL; 164152419Sjulian 164252419Sjulian /* Make a writable copy of address for ng_path_parse() */ 164352419Sjulian strncpy(fullpath, address, sizeof(fullpath) - 1); 164452419Sjulian fullpath[sizeof(fullpath) - 1] = '\0'; 164552419Sjulian 164652419Sjulian /* Parse out node and sequence of hooks */ 164752419Sjulian if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 164871047Sjulian TRAP_ERROR(); 164952419Sjulian return EINVAL; 165052419Sjulian } 165152419Sjulian 165270700Sjulian /* 165370700Sjulian * For an absolute address, jump to the starting node. 165470700Sjulian * Note that this holds a reference on the node for us. 165570700Sjulian * Don't forget to drop the reference if we don't need it. 165670700Sjulian */ 165752419Sjulian if (nodename) { 165870700Sjulian node = ng_name2noderef(here, nodename); 165952419Sjulian if (node == NULL) { 166071047Sjulian TRAP_ERROR(); 166152419Sjulian return (ENOENT); 166252419Sjulian } 166370700Sjulian } else { 166470700Sjulian if (here == NULL) { 166571047Sjulian TRAP_ERROR(); 166670700Sjulian return (EINVAL); 166770700Sjulian } 166852419Sjulian node = here; 166970784Sjulian NG_NODE_REF(node); 167070700Sjulian } 167152419Sjulian 1672219827Sglebius if (path == NULL) { 1673219827Sglebius if (lasthook != NULL) 1674219827Sglebius *lasthook = NULL; 1675219827Sglebius *destp = node; 1676219827Sglebius return (0); 1677219827Sglebius } 1678219827Sglebius 167970700Sjulian /* 1680152451Sglebius * Now follow the sequence of hooks 1681219827Sglebius * 1682219827Sglebius * XXXGL: The path may demolish as we go the sequence, but if 1683219827Sglebius * we hold the topology mutex at critical places, then, I hope, 1684219827Sglebius * we would always have valid pointers in hand, although the 1685219827Sglebius * path behind us may no longer exist. 168670700Sjulian */ 1687219827Sglebius for (;;) { 1688219827Sglebius hook_p hook; 168952419Sjulian char *segment; 169052419Sjulian 169152419Sjulian /* 169252419Sjulian * Break out the next path segment. Replace the dot we just 1693219827Sglebius * found with a NUL; "path" points to the next segment (or the 169452419Sjulian * NUL at the end). 169552419Sjulian */ 1696219827Sglebius for (segment = path; *path != '\0'; path++) { 1697219827Sglebius if (*path == '.') { 1698219827Sglebius *path++ = '\0'; 169952419Sjulian break; 170052419Sjulian } 170152419Sjulian } 170252419Sjulian 170352419Sjulian /* We have a segment, so look for a hook by that name */ 170454096Sarchie hook = ng_findhook(node, segment); 170552419Sjulian 1706219827Sglebius mtx_lock(&ng_topo_mtx); 170752419Sjulian /* Can't get there from here... */ 1708229003Sglebius if (hook == NULL || NG_HOOK_PEER(hook) == NULL || 1709229003Sglebius NG_HOOK_NOT_VALID(hook) || 1710229003Sglebius NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) { 171171047Sjulian TRAP_ERROR(); 171270784Sjulian NG_NODE_UNREF(node); 1713219827Sglebius mtx_unlock(&ng_topo_mtx); 171452419Sjulian return (ENOENT); 171552419Sjulian } 171652419Sjulian 171770700Sjulian /* 1718152451Sglebius * Hop on over to the next node 171970700Sjulian * XXX 1720152451Sglebius * Big race conditions here as hooks and nodes go away 172170700Sjulian * *** Idea.. store an ng_ID_t in each hook and use that 172270700Sjulian * instead of the direct hook in this crawl? 172370700Sjulian */ 172470700Sjulian oldnode = node; 172570784Sjulian if ((node = NG_PEER_NODE(hook))) 172670784Sjulian NG_NODE_REF(node); /* XXX RACE */ 172770784Sjulian NG_NODE_UNREF(oldnode); /* XXX another race */ 172870784Sjulian if (NG_NODE_NOT_VALID(node)) { 172970784Sjulian NG_NODE_UNREF(node); /* XXX more races */ 1730219827Sglebius mtx_unlock(&ng_topo_mtx); 1731219827Sglebius TRAP_ERROR(); 1732219827Sglebius return (ENXIO); 173370700Sjulian } 173452419Sjulian 1735219827Sglebius if (*path == '\0') { 1736219827Sglebius if (lasthook != NULL) { 1737219827Sglebius if (hook != NULL) { 1738219827Sglebius *lasthook = NG_HOOK_PEER(hook); 1739219827Sglebius NG_HOOK_REF(*lasthook); 1740219827Sglebius } else 1741219827Sglebius *lasthook = NULL; 1742219827Sglebius } 1743219827Sglebius mtx_unlock(&ng_topo_mtx); 1744219827Sglebius *destp = node; 1745219827Sglebius return (0); 1746219827Sglebius } 1747219827Sglebius mtx_unlock(&ng_topo_mtx); 174852419Sjulian } 174952419Sjulian} 175052419Sjulian 175170700Sjulian/***************************************************************\ 175270700Sjulian* Input queue handling. 175370700Sjulian* All activities are submitted to the node via the input queue 175470700Sjulian* which implements a multiple-reader/single-writer gate. 1755167677Srwatson* Items which cannot be handled immediately are queued. 175670700Sjulian* 175770700Sjulian* read-write queue locking inline functions * 175870700Sjulian\***************************************************************/ 175970700Sjulian 1760178228Smavstatic __inline void ng_queue_rw(node_p node, item_p item, int rw); 1761178228Smavstatic __inline item_p ng_dequeue(node_p node, int *rw); 1762178228Smavstatic __inline item_p ng_acquire_read(node_p node, item_p item); 1763178228Smavstatic __inline item_p ng_acquire_write(node_p node, item_p item); 1764178228Smavstatic __inline void ng_leave_read(node_p node); 1765178228Smavstatic __inline void ng_leave_write(node_p node); 176670700Sjulian 176752419Sjulian/* 176870700Sjulian * Definition of the bits fields in the ng_queue flag word. 176970700Sjulian * Defined here rather than in netgraph.h because no-one should fiddle 177070700Sjulian * with them. 177170700Sjulian * 177271902Sjulian * The ordering here may be important! don't shuffle these. 177352419Sjulian */ 177470700Sjulian/*- 177570700Sjulian Safety Barrier--------+ (adjustable to suit taste) (not used yet) 177670700Sjulian | 177770700Sjulian V 177870700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+ 1779151973Sglebius | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1780151973Sglebius | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A| 1781151973Sglebius | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W| 178270700Sjulian+-------+-------+-------+-------+-------+-------+-------+-------+ 1783151973Sglebius \___________________________ ____________________________/ | | 1784151973Sglebius V | | 1785151973Sglebius [active reader count] | | 178670700Sjulian | | 1787151973Sglebius Operation Pending -------------------------------+ | 178870700Sjulian | 1789151973Sglebius Active Writer ---------------------------------------+ 179071902Sjulian 1791178039SmavNode queue has such semantics: 1792178039Smav- All flags modifications are atomic. 1793178039Smav- Reader count can be incremented only if there is no writer or pending flags. 1794178039Smav As soon as this can't be done with single operation, it is implemented with 1795178039Smav spin loop and atomic_cmpset(). 1796178039Smav- Writer flag can be set only if there is no any bits set. 1797178039Smav It is implemented with atomic_cmpset(). 1798178039Smav- Pending flag can be set any time, but to avoid collision on queue processing 1799178039Smav all queue fields are protected by the mutex. 1800178039Smav- Queue processing thread reads queue holding the mutex, but releases it while 1801178039Smav processing. When queue is empty pending flag is removed. 1802178039Smav*/ 180371902Sjulian 1804151973Sglebius#define WRITER_ACTIVE 0x00000001 1805151973Sglebius#define OP_PENDING 0x00000002 1806151973Sglebius#define READER_INCREMENT 0x00000004 1807151973Sglebius#define READER_MASK 0xfffffffc /* Not valid if WRITER_ACTIVE is set */ 1808151973Sglebius#define SAFETY_BARRIER 0x00100000 /* 128K items queued should be enough */ 180971902Sjulian 181071902Sjulian/* Defines of more elaborate states on the queue */ 1811151973Sglebius/* Mask of bits a new read cares about */ 1812151973Sglebius#define NGQ_RMASK (WRITER_ACTIVE|OP_PENDING) 181371902Sjulian 1814151973Sglebius/* Mask of bits a new write cares about */ 181571902Sjulian#define NGQ_WMASK (NGQ_RMASK|READER_MASK) 181671902Sjulian 1817151973Sglebius/* Test to decide if there is something on the queue. */ 1818151973Sglebius#define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING) 181971902Sjulian 1820151973Sglebius/* How to decide what the next queued item is. */ 1821178228Smav#define HEAD_IS_READER(QP) NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue)) 1822178228Smav#define HEAD_IS_WRITER(QP) NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */ 1823151973Sglebius 1824151973Sglebius/* Read the status to decide if the next item on the queue can now run. */ 1825151973Sglebius#define QUEUED_READER_CAN_PROCEED(QP) \ 1826151973Sglebius (((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0) 1827151973Sglebius#define QUEUED_WRITER_CAN_PROCEED(QP) \ 1828151973Sglebius (((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0) 1829151973Sglebius 183071902Sjulian/* Is there a chance of getting ANY work off the queue? */ 1831151973Sglebius#define NEXT_QUEUED_ITEM_CAN_PROCEED(QP) \ 1832151973Sglebius ((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) : \ 1833177953Smav QUEUED_WRITER_CAN_PROCEED(QP)) 183471902Sjulian 1835151238Sglebius#define NGQRW_R 0 1836151238Sglebius#define NGQRW_W 1 1837151238Sglebius 1838178228Smav#define NGQ2_WORKQ 0x00000001 1839178228Smav 184070700Sjulian/* 184170700Sjulian * Taking into account the current state of the queue and node, possibly take 184270700Sjulian * the next entry off the queue and return it. Return NULL if there was 184370700Sjulian * nothing we could return, either because there really was nothing there, or 184470700Sjulian * because the node was in a state where it cannot yet process the next item 184570700Sjulian * on the queue. 184670700Sjulian */ 184770700Sjulianstatic __inline item_p 1848178228Smavng_dequeue(node_p node, int *rw) 184970700Sjulian{ 185070700Sjulian item_p item; 1851178228Smav struct ng_queue *ngq = &node->nd_input_queue; 185271902Sjulian 1853178039Smav /* This MUST be called with the mutex held. */ 1854139039Sglebius mtx_assert(&ngq->q_mtx, MA_OWNED); 1855178039Smav 1856178039Smav /* If there is nothing queued, then just return. */ 1857151973Sglebius if (!QUEUE_ACTIVE(ngq)) { 1858154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; " 1859154275Sglebius "queue flags 0x%lx", __func__, 1860178228Smav node->nd_ID, node, ngq->q_flags); 1861151973Sglebius return (NULL); 1862151973Sglebius } 1863139039Sglebius 1864151973Sglebius /* 1865151973Sglebius * From here, we can assume there is a head item. 1866151973Sglebius * We need to find out what it is and if it can be dequeued, given 1867151973Sglebius * the current state of the node. 1868151973Sglebius */ 1869151973Sglebius if (HEAD_IS_READER(ngq)) { 1870177953Smav while (1) { 1871177953Smav long t = ngq->q_flags; 1872177953Smav if (t & WRITER_ACTIVE) { 1873178039Smav /* There is writer, reader can't proceed. */ 1874229003Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) queued " 1875229003Sglebius "reader can't proceed; queue flags 0x%lx", 1876229003Sglebius __func__, node->nd_ID, node, t); 1877177953Smav return (NULL); 1878177953Smav } 1879178228Smav if (atomic_cmpset_acq_int(&ngq->q_flags, t, 1880177953Smav t + READER_INCREMENT)) 1881177953Smav break; 1882177953Smav cpu_spinwait(); 1883151973Sglebius } 1884178039Smav /* We have got reader lock for the node. */ 1885151238Sglebius *rw = NGQRW_R; 1886178228Smav } else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING, 1887177953Smav OP_PENDING + WRITER_ACTIVE)) { 1888178039Smav /* We have got writer lock for the node. */ 1889151238Sglebius *rw = NGQRW_W; 189070700Sjulian } else { 1891178039Smav /* There is somebody other, writer can't proceed. */ 1892229003Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer can't " 1893229003Sglebius "proceed; queue flags 0x%lx", __func__, node->nd_ID, node, 1894229003Sglebius ngq->q_flags); 1895151973Sglebius return (NULL); 189670700Sjulian } 189752419Sjulian 189870700Sjulian /* 189970700Sjulian * Now we dequeue the request (whatever it may be) and correct the 190070700Sjulian * pending flags and the next and last pointers. 190170700Sjulian */ 1902178228Smav item = STAILQ_FIRST(&ngq->queue); 1903178228Smav STAILQ_REMOVE_HEAD(&ngq->queue, el_next); 1904178228Smav if (STAILQ_EMPTY(&ngq->queue)) 1905178228Smav atomic_clear_int(&ngq->q_flags, OP_PENDING); 1906229003Sglebius CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; queue " 1907229003Sglebius "flags 0x%lx", __func__, node->nd_ID, node, item, *rw ? "WRITER" : 1908229003Sglebius "READER", ngq->q_flags); 190970700Sjulian return (item); 191070700Sjulian} 191152419Sjulian 191270700Sjulian/* 1913178039Smav * Queue a packet to be picked up later by someone else. 1914178039Smav * If the queue could be run now, add node to the queue handler's worklist. 191570700Sjulian */ 191670700Sjulianstatic __inline void 1917178228Smavng_queue_rw(node_p node, item_p item, int rw) 191870700Sjulian{ 1919178228Smav struct ng_queue *ngq = &node->nd_input_queue; 1920151973Sglebius if (rw == NGQRW_W) 1921151973Sglebius NGI_SET_WRITER(item); 1922151973Sglebius else 1923151973Sglebius NGI_SET_READER(item); 1924177953Smav 1925177953Smav NG_QUEUE_LOCK(ngq); 1926177953Smav /* Set OP_PENDING flag and enqueue the item. */ 1927178228Smav atomic_set_int(&ngq->q_flags, OP_PENDING); 1928178228Smav STAILQ_INSERT_TAIL(&ngq->queue, item, el_next); 1929177953Smav 1930154275Sglebius CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__, 1931178228Smav node->nd_ID, node, item, rw ? "WRITER" : "READER" ); 1932229003Sglebius 193370700Sjulian /* 1934151973Sglebius * We can take the worklist lock with the node locked 1935151973Sglebius * BUT NOT THE REVERSE! 1936151973Sglebius */ 1937151973Sglebius if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 1938178228Smav ng_worklist_add(node); 1939177953Smav NG_QUEUE_UNLOCK(ngq); 194070700Sjulian} 194152419Sjulian 1942178039Smav/* Acquire reader lock on node. If node is busy, queue the packet. */ 194370700Sjulianstatic __inline item_p 1944178228Smavng_acquire_read(node_p node, item_p item) 194552419Sjulian{ 1946178228Smav KASSERT(node != &ng_deadnode, 1947151974Sglebius ("%s: working on deadnode", __func__)); 194852419Sjulian 1949177953Smav /* Reader needs node without writer and pending items. */ 1950229003Sglebius for (;;) { 1951178228Smav long t = node->nd_input_queue.q_flags; 1952177953Smav if (t & NGQ_RMASK) 1953177953Smav break; /* Node is not ready for reader. */ 1954229003Sglebius if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, t, 1955229003Sglebius t + READER_INCREMENT)) { 1956177953Smav /* Successfully grabbed node */ 1957177953Smav CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", 1958178228Smav __func__, node->nd_ID, node, item); 1959177953Smav return (item); 1960177953Smav } 1961177953Smav cpu_spinwait(); 1962177953Smav }; 196370700Sjulian 1964177953Smav /* Queue the request for later. */ 1965178228Smav ng_queue_rw(node, item, NGQRW_R); 196670700Sjulian 1967148236Sglebius return (NULL); 196870700Sjulian} 196970700Sjulian 1970178039Smav/* Acquire writer lock on node. If node is busy, queue the packet. */ 197170700Sjulianstatic __inline item_p 1972178228Smavng_acquire_write(node_p node, item_p item) 197370700Sjulian{ 1974178228Smav KASSERT(node != &ng_deadnode, 1975151974Sglebius ("%s: working on deadnode", __func__)); 1976151974Sglebius 1977177953Smav /* Writer needs completely idle node. */ 1978229003Sglebius if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, 0, 1979229003Sglebius WRITER_ACTIVE)) { 1980177953Smav /* Successfully grabbed node */ 1981154275Sglebius CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", 1982178228Smav __func__, node->nd_ID, node, item); 198370700Sjulian return (item); 198452419Sjulian } 198552419Sjulian 1986177953Smav /* Queue the request for later. */ 1987178228Smav ng_queue_rw(node, item, NGQRW_W); 198870700Sjulian 1989148236Sglebius return (NULL); 199070700Sjulian} 199170700Sjulian 1992167385Sjulian#if 0 1993167385Sjulianstatic __inline item_p 1994178228Smavng_upgrade_write(node_p node, item_p item) 1995167385Sjulian{ 1996178228Smav struct ng_queue *ngq = &node->nd_input_queue; 1997178228Smav KASSERT(node != &ng_deadnode, 1998167385Sjulian ("%s: working on deadnode", __func__)); 1999167385Sjulian 2000167385Sjulian NGI_SET_WRITER(item); 2001167385Sjulian 2002178228Smav NG_QUEUE_LOCK(ngq); 2003167385Sjulian 2004167385Sjulian /* 2005167385Sjulian * There will never be no readers as we are there ourselves. 2006167385Sjulian * Set the WRITER_ACTIVE flags ASAP to block out fast track readers. 2007167385Sjulian * The caller we are running from will call ng_leave_read() 2008167385Sjulian * soon, so we must account for that. We must leave again with the 2009167385Sjulian * READER lock. If we find other readers, then 2010167385Sjulian * queue the request for later. However "later" may be rignt now 2011167385Sjulian * if there are no readers. We don't really care if there are queued 2012167385Sjulian * items as we will bypass them anyhow. 2013167385Sjulian */ 2014178228Smav atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT); 2015178228Smav if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) { 2016178228Smav NG_QUEUE_UNLOCK(ngq); 2017167385Sjulian 2018167385Sjulian /* It's just us, act on the item. */ 2019167385Sjulian /* will NOT drop writer lock when done */ 2020167385Sjulian ng_apply_item(node, item, 0); 2021167385Sjulian 2022167385Sjulian /* 2023229003Sglebius * Having acted on the item, atomically 2024229003Sglebius * downgrade back to READER and finish up. 2025167385Sjulian */ 2026229003Sglebius atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE); 2027167385Sjulian 2028167385Sjulian /* Our caller will call ng_leave_read() */ 2029167385Sjulian return; 2030167385Sjulian } 2031167385Sjulian /* 2032167385Sjulian * It's not just us active, so queue us AT THE HEAD. 2033167385Sjulian * "Why?" I hear you ask. 2034167385Sjulian * Put us at the head of the queue as we've already been 2035167385Sjulian * through it once. If there is nothing else waiting, 2036167385Sjulian * set the correct flags. 2037167385Sjulian */ 2038178228Smav if (STAILQ_EMPTY(&ngq->queue)) { 2039167385Sjulian /* We've gone from, 0 to 1 item in the queue */ 2040178228Smav atomic_set_int(&ngq->q_flags, OP_PENDING); 2041167385Sjulian 2042167385Sjulian CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__, 2043178228Smav node->nd_ID, node); 2044167385Sjulian }; 2045178228Smav STAILQ_INSERT_HEAD(&ngq->queue, item, el_next); 2046178228Smav CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER", 2047178228Smav __func__, node->nd_ID, node, item ); 2048167385Sjulian 2049167385Sjulian /* Reverse what we did above. That downgrades us back to reader */ 2050178228Smav atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE); 2051177953Smav if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2052178228Smav ng_worklist_add(node); 2053178228Smav NG_QUEUE_UNLOCK(ngq); 2054167385Sjulian 2055167385Sjulian return; 2056167385Sjulian} 2057167385Sjulian#endif 2058167385Sjulian 2059178039Smav/* Release reader lock. */ 206070700Sjulianstatic __inline void 2061178228Smavng_leave_read(node_p node) 206270700Sjulian{ 2063178228Smav atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT); 206470700Sjulian} 206570700Sjulian 2066178039Smav/* Release writer lock. */ 206770700Sjulianstatic __inline void 2068178228Smavng_leave_write(node_p node) 206970700Sjulian{ 2070178228Smav atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE); 207170700Sjulian} 207270700Sjulian 2073178039Smav/* Purge node queue. Called on node shutdown. */ 207470700Sjulianstatic void 2075178228Smavng_flush_input_queue(node_p node) 207670700Sjulian{ 2077178228Smav struct ng_queue *ngq = &node->nd_input_queue; 207870700Sjulian item_p item; 2079151973Sglebius 2080168049Swkoszek NG_QUEUE_LOCK(ngq); 2081178228Smav while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) { 2082178228Smav STAILQ_REMOVE_HEAD(&ngq->queue, el_next); 2083178228Smav if (STAILQ_EMPTY(&ngq->queue)) 2084178228Smav atomic_clear_int(&ngq->q_flags, OP_PENDING); 2085168049Swkoszek NG_QUEUE_UNLOCK(ngq); 208670700Sjulian 2087151973Sglebius /* If the item is supplying a callback, call it with an error */ 2088177071Smav if (item->apply != NULL) { 2089177071Smav if (item->depth == 1) 2090177071Smav item->apply->error = ENOENT; 2091177071Smav if (refcount_release(&item->apply->refs)) { 2092177071Smav (*item->apply->apply)(item->apply->context, 2093177071Smav item->apply->error); 2094177071Smav } 2095151283Sglebius } 2096132369Sjulian NG_FREE_ITEM(item); 2097168049Swkoszek NG_QUEUE_LOCK(ngq); 209870700Sjulian } 2099168049Swkoszek NG_QUEUE_UNLOCK(ngq); 210070700Sjulian} 210170700Sjulian 210270700Sjulian/*********************************************************************** 210370700Sjulian* Externally visible method for sending or queueing messages or data. 210470700Sjulian***********************************************************************/ 210570700Sjulian 210670700Sjulian/* 210771849Sjulian * The module code should have filled out the item correctly by this stage: 210870700Sjulian * Common: 210970700Sjulian * reference to destination node. 211070700Sjulian * Reference to destination rcv hook if relevant. 2111172806Smav * apply pointer must be or NULL or reference valid struct ng_apply_info. 211270700Sjulian * Data: 211370700Sjulian * pointer to mbuf 211470700Sjulian * Control_Message: 211570700Sjulian * pointer to msg. 211670700Sjulian * ID of original sender node. (return address) 211771849Sjulian * Function: 211871849Sjulian * Function pointer 211971849Sjulian * void * argument 212071849Sjulian * integer argument 212170700Sjulian * 212270700Sjulian * The nodes have several routines and macros to help with this task: 212370700Sjulian */ 212470700Sjulian 212570700Sjulianint 2126146281Sglebiusng_snd_item(item_p item, int flags) 212770700Sjulian{ 2128172806Smav hook_p hook; 2129172806Smav node_p node; 2130146281Sglebius int queue, rw; 2131172806Smav struct ng_queue *ngq; 2132151256Sglebius int error = 0; 213370700Sjulian 2134176046Smav /* We are sending item, so it must be present! */ 2135176046Smav KASSERT(item != NULL, ("ng_snd_item: item is NULL")); 2136172806Smav 213770784Sjulian#ifdef NETGRAPH_DEBUG 2138152451Sglebius _ngi_check(item, __FILE__, __LINE__); 213970700Sjulian#endif 214070700Sjulian 2141176046Smav /* Item was sent once more, postpone apply() call. */ 2142172806Smav if (item->apply) 2143172806Smav refcount_acquire(&item->apply->refs); 2144146281Sglebius 2145172806Smav node = NGI_NODE(item); 2146176046Smav /* Node is never optional. */ 2147176046Smav KASSERT(node != NULL, ("ng_snd_item: node is NULL")); 2148172806Smav 2149175850Smav hook = NGI_HOOK(item); 2150176046Smav /* Valid hook and mbuf are mandatory for data. */ 2151176046Smav if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) { 2152176046Smav KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL")); 2153131123Sjulian if (NGI_M(item) == NULL) 2154172806Smav ERROUT(EINVAL); 215570700Sjulian CHECK_DATA_MBUF(NGI_M(item)); 215669922Sjulian } 2157149505Sglebius 215870700Sjulian /* 2159176046Smav * If the item or the node specifies single threading, force 2160176046Smav * writer semantics. Similarly, the node may say one hook always 2161176046Smav * produces writers. These are overrides. 216270700Sjulian */ 2163176567Smav if (((item->el_flags & NGQF_RW) == NGQF_WRITER) || 2164176046Smav (node->nd_flags & NGF_FORCE_WRITER) || 2165176046Smav (hook && (hook->hk_flags & HK_FORCE_WRITER))) { 2166176046Smav rw = NGQRW_W; 2167176046Smav } else { 2168176046Smav rw = NGQRW_R; 2169176046Smav } 2170149505Sglebius 2171175847Smav /* 2172194012Szec * If sender or receiver requests queued delivery, or call graph 2173194012Szec * loops back from outbound to inbound path, or stack usage 2174175847Smav * level is dangerous - enqueue message. 2175175847Smav */ 2176175847Smav if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) { 2177175847Smav queue = 1; 2178194012Szec } else if (hook && (hook->hk_flags & HK_TO_INBOUND) && 2179194012Szec curthread->td_ng_outbound) { 2180194012Szec queue = 1; 2181176046Smav } else { 2182176046Smav queue = 0; 2183175847Smav#ifdef GET_STACK_USAGE 2184175868Smav /* 2185175871Smarck * Most of netgraph nodes have small stack consumption and 2186176046Smav * for them 25% of free stack space is more than enough. 2187175868Smav * Nodes/hooks with higher stack usage should be marked as 2188175889Smarck * HI_STACK. For them 50% of stack will be guaranteed then. 2189176046Smav * XXX: Values 25% and 50% are completely empirical. 2190175868Smav */ 2191176046Smav size_t st, su, sl; 2192175847Smav GET_STACK_USAGE(st, su); 2193176046Smav sl = st - su; 2194229003Sglebius if ((sl * 4 < st) || ((sl * 2 < st) && 2195229003Sglebius ((node->nd_flags & NGF_HI_STACK) || (hook && 2196229003Sglebius (hook->hk_flags & HK_HI_STACK))))) 2197175847Smav queue = 1; 2198176046Smav#endif 2199175847Smav } 2200175847Smav 220170700Sjulian if (queue) { 2202177953Smav item->depth = 1; 220370700Sjulian /* Put it on the queue for that node*/ 2204178228Smav ng_queue_rw(node, item, rw); 2205176046Smav return ((flags & NG_PROGRESS) ? EINPROGRESS : 0); 220670700Sjulian } 220769922Sjulian 220870700Sjulian /* 220970700Sjulian * We already decided how we will be queueud or treated. 221070700Sjulian * Try get the appropriate operating permission. 221170700Sjulian */ 2212151256Sglebius if (rw == NGQRW_R) 2213178228Smav item = ng_acquire_read(node, item); 2214151256Sglebius else 2215178228Smav item = ng_acquire_write(node, item); 221652419Sjulian 2217176046Smav /* Item was queued while trying to get permission. */ 2218176046Smav if (item == NULL) 2219176046Smav return ((flags & NG_PROGRESS) ? EINPROGRESS : 0); 222052419Sjulian 222174078Sjulian NGI_GET_NODE(item, node); /* zaps stored node */ 222252419Sjulian 2223177071Smav item->depth++; 2224170180Sglebius error = ng_apply_item(node, item, rw); /* drops r/w lock when done */ 222574078Sjulian 2226177953Smav /* If something is waiting on queue and ready, schedule it. */ 2227178228Smav ngq = &node->nd_input_queue; 2228177953Smav if (QUEUE_ACTIVE(ngq)) { 2229177953Smav NG_QUEUE_LOCK(ngq); 2230177953Smav if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2231178228Smav ng_worklist_add(node); 2232177953Smav NG_QUEUE_UNLOCK(ngq); 2233177953Smav } 2234177953Smav 223552419Sjulian /* 2236177953Smav * Node may go away as soon as we remove the reference. 2237177953Smav * Whatever we do, DO NOT access the node again! 223874078Sjulian */ 2239177953Smav NG_NODE_UNREF(node); 224074078Sjulian 224171849Sjulian return (error); 2242172806Smav 2243172806Smavdone: 2244176046Smav /* If was not sent, apply callback here. */ 2245177071Smav if (item->apply != NULL) { 2246177071Smav if (item->depth == 0 && error != 0) 2247177071Smav item->apply->error = error; 2248177071Smav if (refcount_release(&item->apply->refs)) { 2249177071Smav (*item->apply->apply)(item->apply->context, 2250177071Smav item->apply->error); 2251177071Smav } 2252177071Smav } 2253175850Smav 2254172806Smav NG_FREE_ITEM(item); 2255172806Smav return (error); 225652419Sjulian} 225752419Sjulian 225852419Sjulian/* 225970700Sjulian * We have an item that was possibly queued somewhere. 226070700Sjulian * It should contain all the information needed 226170700Sjulian * to run it on the appropriate node/hook. 2262172806Smav * If there is apply pointer and we own the last reference, call apply(). 226352419Sjulian */ 2264170180Sglebiusstatic int 2265151238Sglebiusng_apply_item(node_p node, item_p item, int rw) 226652419Sjulian{ 226770700Sjulian hook_p hook; 226870700Sjulian ng_rcvdata_t *rcvdata; 226971885Sjulian ng_rcvmsg_t *rcvmsg; 2270172806Smav struct ng_apply_info *apply; 2271177071Smav int error = 0, depth; 227252419Sjulian 2273176046Smav /* Node and item are never optional. */ 2274176046Smav KASSERT(node != NULL, ("ng_apply_item: node is NULL")); 2275176046Smav KASSERT(item != NULL, ("ng_apply_item: item is NULL")); 2276176046Smav 227771849Sjulian NGI_GET_HOOK(item, hook); /* clears stored hook */ 227870784Sjulian#ifdef NETGRAPH_DEBUG 2279152451Sglebius _ngi_check(item, __FILE__, __LINE__); 228070700Sjulian#endif 2281147774Sglebius 2282172806Smav apply = item->apply; 2283177071Smav depth = item->depth; 2284147774Sglebius 228571047Sjulian switch (item->el_flags & NGQF_TYPE) { 228670700Sjulian case NGQF_DATA: 228770700Sjulian /* 228870700Sjulian * Check things are still ok as when we were queued. 228970700Sjulian */ 2290176046Smav KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL")); 2291176046Smav if (NG_HOOK_NOT_VALID(hook) || 2292176046Smav NG_NODE_NOT_VALID(node)) { 2293167402Sjulian error = EIO; 229470700Sjulian NG_FREE_ITEM(item); 229571885Sjulian break; 229670700Sjulian } 229771885Sjulian /* 229871885Sjulian * If no receive method, just silently drop it. 2299229003Sglebius * Give preference to the hook over-ride method. 230071885Sjulian */ 2301229003Sglebius if ((!(rcvdata = hook->hk_rcvdata)) && 2302229003Sglebius (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) { 230371885Sjulian error = 0; 230471885Sjulian NG_FREE_ITEM(item); 230571885Sjulian break; 230671885Sjulian } 2307167402Sjulian error = (*rcvdata)(hook, item); 230870700Sjulian break; 230970700Sjulian case NGQF_MESG: 2310175850Smav if (hook && NG_HOOK_NOT_VALID(hook)) { 2311175850Smav /* 2312175850Smav * The hook has been zapped then we can't use it. 2313175850Smav * Immediately drop its reference. 2314175850Smav * The message may not need it. 2315175850Smav */ 2316175850Smav NG_HOOK_UNREF(hook); 2317175850Smav hook = NULL; 231870700Sjulian } 231970700Sjulian /* 232070700Sjulian * Similarly, if the node is a zombie there is 232170700Sjulian * nothing we can do with it, drop everything. 232270700Sjulian */ 232370784Sjulian if (NG_NODE_NOT_VALID(node)) { 232471047Sjulian TRAP_ERROR(); 2325167402Sjulian error = EINVAL; 232670700Sjulian NG_FREE_ITEM(item); 2327175850Smav break; 232870700Sjulian } 2329175850Smav /* 2330175850Smav * Call the appropriate message handler for the object. 2331175850Smav * It is up to the message handler to free the message. 2332175850Smav * If it's a generic message, handle it generically, 2333175850Smav * otherwise call the type's message handler (if it exists). 2334175850Smav * XXX (race). Remember that a queued message may 2335175850Smav * reference a node or hook that has just been 2336175850Smav * invalidated. It will exist as the queue code 2337175850Smav * is holding a reference, but.. 2338175850Smav */ 2339175850Smav if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) && 2340175850Smav ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) { 2341175850Smav error = ng_generic_msg(node, item, hook); 2342175850Smav break; 2343175850Smav } 2344175850Smav if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) && 2345175850Smav (!(rcvmsg = node->nd_type->rcvmsg))) { 2346175850Smav TRAP_ERROR(); 2347175850Smav error = 0; 2348175850Smav NG_FREE_ITEM(item); 2349175850Smav break; 2350175850Smav } 2351175850Smav error = (*rcvmsg)(node, item, hook); 235270700Sjulian break; 235371047Sjulian case NGQF_FN: 2354173605Sglebius case NGQF_FN2: 235571047Sjulian /* 2356182995Smav * In the case of the shutdown message we allow it to hit 235771849Sjulian * even if the node is invalid. 235871047Sjulian */ 2359182995Smav if (NG_NODE_NOT_VALID(node) && 2360182995Smav NGI_FN(item) != &ng_rmnode) { 236171047Sjulian TRAP_ERROR(); 2362167402Sjulian error = EINVAL; 2363143384Sglebius NG_FREE_ITEM(item); 236471047Sjulian break; 236571047Sjulian } 2366182995Smav /* Same is about some internal functions and invalid hook. */ 2367182995Smav if (hook && NG_HOOK_NOT_VALID(hook) && 2368182995Smav NGI_FN2(item) != &ng_con_part2 && 2369182995Smav NGI_FN2(item) != &ng_con_part3 && 2370182995Smav NGI_FN(item) != &ng_rmhook_part2) { 2371182995Smav TRAP_ERROR(); 2372182995Smav error = EINVAL; 2373182995Smav NG_FREE_ITEM(item); 2374182995Smav break; 2375182995Smav } 2376182995Smav 2377173605Sglebius if ((item->el_flags & NGQF_TYPE) == NGQF_FN) { 2378173605Sglebius (*NGI_FN(item))(node, hook, NGI_ARG1(item), 2379173605Sglebius NGI_ARG2(item)); 2380172806Smav NG_FREE_ITEM(item); 2381173605Sglebius } else /* it is NGQF_FN2 */ 2382173605Sglebius error = (*NGI_FN2(item))(node, item, hook); 2383172806Smav break; 238470700Sjulian } 238570700Sjulian /* 238670700Sjulian * We held references on some of the resources 238770700Sjulian * that we took from the item. Now that we have 238870700Sjulian * finished doing everything, drop those references. 238970700Sjulian */ 2390175850Smav if (hook) 239170784Sjulian NG_HOOK_UNREF(hook); 239270700Sjulian 2393176046Smav if (rw == NGQRW_R) 2394178228Smav ng_leave_read(node); 2395176046Smav else 2396178228Smav ng_leave_write(node); 2397147774Sglebius 2398147774Sglebius /* Apply callback. */ 2399177071Smav if (apply != NULL) { 2400177071Smav if (depth == 1 && error != 0) 2401177071Smav apply->error = error; 2402177071Smav if (refcount_release(&apply->refs)) 2403177071Smav (*apply->apply)(apply->context, apply->error); 2404177071Smav } 2405147774Sglebius 2406170180Sglebius return (error); 240770700Sjulian} 240870700Sjulian 240970700Sjulian/*********************************************************************** 241070700Sjulian * Implement the 'generic' control messages 241170700Sjulian ***********************************************************************/ 241270700Sjulianstatic int 241370700Sjulianng_generic_msg(node_p here, item_p item, hook_p lasthook) 241470700Sjulian{ 241570700Sjulian int error = 0; 241670700Sjulian struct ng_mesg *msg; 241770700Sjulian struct ng_mesg *resp = NULL; 241870700Sjulian 241970700Sjulian NGI_GET_MSG(item, msg); 242052419Sjulian if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 242171047Sjulian TRAP_ERROR(); 242270700Sjulian error = EINVAL; 242370700Sjulian goto out; 242452419Sjulian } 242552419Sjulian switch (msg->header.cmd) { 242652419Sjulian case NGM_SHUTDOWN: 242771849Sjulian ng_rmnode(here, NULL, NULL, 0); 242852419Sjulian break; 242952419Sjulian case NGM_MKPEER: 243052419Sjulian { 243152419Sjulian struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 243252419Sjulian 243352419Sjulian if (msg->header.arglen != sizeof(*mkp)) { 243471047Sjulian TRAP_ERROR(); 243570700Sjulian error = EINVAL; 243670700Sjulian break; 243752419Sjulian } 243852419Sjulian mkp->type[sizeof(mkp->type) - 1] = '\0'; 243952419Sjulian mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 244052419Sjulian mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 244152419Sjulian error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 244252419Sjulian break; 244352419Sjulian } 244452419Sjulian case NGM_CONNECT: 244552419Sjulian { 244652419Sjulian struct ngm_connect *const con = 244752419Sjulian (struct ngm_connect *) msg->data; 244852419Sjulian node_p node2; 244952419Sjulian 245052419Sjulian if (msg->header.arglen != sizeof(*con)) { 245171047Sjulian TRAP_ERROR(); 245270700Sjulian error = EINVAL; 245370700Sjulian break; 245452419Sjulian } 245552419Sjulian con->path[sizeof(con->path) - 1] = '\0'; 245652419Sjulian con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 245752419Sjulian con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 245870700Sjulian /* Don't forget we get a reference.. */ 245970700Sjulian error = ng_path2noderef(here, con->path, &node2, NULL); 246052419Sjulian if (error) 246152419Sjulian break; 2462172806Smav error = ng_con_nodes(item, here, con->ourhook, 2463172806Smav node2, con->peerhook); 246470784Sjulian NG_NODE_UNREF(node2); 246552419Sjulian break; 246652419Sjulian } 246752419Sjulian case NGM_NAME: 246852419Sjulian { 246952419Sjulian struct ngm_name *const nam = (struct ngm_name *) msg->data; 247052419Sjulian 247152419Sjulian if (msg->header.arglen != sizeof(*nam)) { 247271047Sjulian TRAP_ERROR(); 247370700Sjulian error = EINVAL; 247470700Sjulian break; 247552419Sjulian } 247652419Sjulian nam->name[sizeof(nam->name) - 1] = '\0'; 247752419Sjulian error = ng_name_node(here, nam->name); 247852419Sjulian break; 247952419Sjulian } 248052419Sjulian case NGM_RMHOOK: 248152419Sjulian { 248252419Sjulian struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 248352419Sjulian hook_p hook; 248452419Sjulian 248552419Sjulian if (msg->header.arglen != sizeof(*rmh)) { 248671047Sjulian TRAP_ERROR(); 248770700Sjulian error = EINVAL; 248870700Sjulian break; 248952419Sjulian } 249052419Sjulian rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 249154096Sarchie if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 249252419Sjulian ng_destroy_hook(hook); 249352419Sjulian break; 249452419Sjulian } 249552419Sjulian case NGM_NODEINFO: 249652419Sjulian { 249752419Sjulian struct nodeinfo *ni; 249852419Sjulian 249970700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT); 250052419Sjulian if (resp == NULL) { 250152419Sjulian error = ENOMEM; 250252419Sjulian break; 250352419Sjulian } 250452419Sjulian 250552419Sjulian /* Fill in node info */ 250670700Sjulian ni = (struct nodeinfo *) resp->data; 250770784Sjulian if (NG_NODE_HAS_NAME(here)) 2508125028Sharti strcpy(ni->name, NG_NODE_NAME(here)); 2509125028Sharti strcpy(ni->type, here->nd_type->name); 251052722Sjulian ni->id = ng_node2ID(here); 251170784Sjulian ni->hooks = here->nd_numhooks; 251252419Sjulian break; 251352419Sjulian } 251452419Sjulian case NGM_LISTHOOKS: 251552419Sjulian { 251670784Sjulian const int nhooks = here->nd_numhooks; 251752419Sjulian struct hooklist *hl; 251852419Sjulian struct nodeinfo *ni; 251952419Sjulian hook_p hook; 252052419Sjulian 252152419Sjulian /* Get response struct */ 2522229003Sglebius NG_MKRESPONSE(resp, msg, sizeof(*hl) + 2523229003Sglebius (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 252452419Sjulian if (resp == NULL) { 252552419Sjulian error = ENOMEM; 252652419Sjulian break; 252752419Sjulian } 252870700Sjulian hl = (struct hooklist *) resp->data; 252952419Sjulian ni = &hl->nodeinfo; 253052419Sjulian 253152419Sjulian /* Fill in node info */ 253270784Sjulian if (NG_NODE_HAS_NAME(here)) 2533125028Sharti strcpy(ni->name, NG_NODE_NAME(here)); 2534125028Sharti strcpy(ni->type, here->nd_type->name); 253552722Sjulian ni->id = ng_node2ID(here); 253652419Sjulian 253752419Sjulian /* Cycle through the linked list of hooks */ 253852419Sjulian ni->hooks = 0; 253970784Sjulian LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) { 254052419Sjulian struct linkinfo *const link = &hl->link[ni->hooks]; 254152419Sjulian 254252419Sjulian if (ni->hooks >= nhooks) { 254352419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 254487599Sobrien __func__, "hooks"); 254552419Sjulian break; 254652419Sjulian } 254770784Sjulian if (NG_HOOK_NOT_VALID(hook)) 254852419Sjulian continue; 2549125028Sharti strcpy(link->ourhook, NG_HOOK_NAME(hook)); 2550125028Sharti strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook)); 255170784Sjulian if (NG_PEER_NODE_NAME(hook)[0] != '\0') 2552125028Sharti strcpy(link->nodeinfo.name, 2553125028Sharti NG_PEER_NODE_NAME(hook)); 2554125028Sharti strcpy(link->nodeinfo.type, 2555125028Sharti NG_PEER_NODE(hook)->nd_type->name); 255670784Sjulian link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook)); 255770784Sjulian link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks; 255852419Sjulian ni->hooks++; 255952419Sjulian } 256052419Sjulian break; 256152419Sjulian } 256252419Sjulian 256352419Sjulian case NGM_LISTNAMES: 256452419Sjulian case NGM_LISTNODES: 256552419Sjulian { 256652419Sjulian const int unnamed = (msg->header.cmd == NGM_LISTNODES); 256752419Sjulian struct namelist *nl; 256852419Sjulian node_p node; 2569176802Smav int num = 0, i; 257052419Sjulian 2571176802Smav mtx_lock(&ng_namehash_mtx); 257252419Sjulian /* Count number of nodes */ 2573176802Smav for (i = 0; i < NG_NAME_HASH_SIZE; i++) { 2574181803Sbz LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) { 2575176802Smav if (NG_NODE_IS_VALID(node) && 2576176802Smav (unnamed || NG_NODE_HAS_NAME(node))) { 2577176802Smav num++; 2578176802Smav } 257970912Sjulian } 258052419Sjulian } 2581176802Smav mtx_unlock(&ng_namehash_mtx); 258252419Sjulian 258352419Sjulian /* Get response struct */ 2584229003Sglebius NG_MKRESPONSE(resp, msg, sizeof(*nl) + 2585229003Sglebius (num * sizeof(struct nodeinfo)), M_NOWAIT); 258652419Sjulian if (resp == NULL) { 258752419Sjulian error = ENOMEM; 258852419Sjulian break; 258952419Sjulian } 259070700Sjulian nl = (struct namelist *) resp->data; 259152419Sjulian 259252419Sjulian /* Cycle through the linked list of nodes */ 259352419Sjulian nl->numnames = 0; 2594176802Smav mtx_lock(&ng_namehash_mtx); 2595176802Smav for (i = 0; i < NG_NAME_HASH_SIZE; i++) { 2596181803Sbz LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) { 2597176802Smav struct nodeinfo *const np = 2598176802Smav &nl->nodeinfo[nl->numnames]; 259952419Sjulian 2600176802Smav if (NG_NODE_NOT_VALID(node)) 2601176802Smav continue; 2602176802Smav if (!unnamed && (! NG_NODE_HAS_NAME(node))) 2603176802Smav continue; 2604176802Smav if (nl->numnames >= num) { 2605176802Smav log(LOG_ERR, "%s: number of nodes changed\n", 2606176802Smav __func__); 2607176802Smav break; 2608176802Smav } 2609176802Smav if (NG_NODE_HAS_NAME(node)) 2610176802Smav strcpy(np->name, NG_NODE_NAME(node)); 2611176802Smav strcpy(np->type, node->nd_type->name); 2612176802Smav np->id = ng_node2ID(node); 2613176802Smav np->hooks = node->nd_numhooks; 2614176802Smav nl->numnames++; 261552419Sjulian } 261652419Sjulian } 2617176802Smav mtx_unlock(&ng_namehash_mtx); 261852419Sjulian break; 261952419Sjulian } 262052419Sjulian 262152419Sjulian case NGM_LISTTYPES: 262252419Sjulian { 262352419Sjulian struct typelist *tl; 262452419Sjulian struct ng_type *type; 262552419Sjulian int num = 0; 262652419Sjulian 262772200Sbmilekic mtx_lock(&ng_typelist_mtx); 262852419Sjulian /* Count number of types */ 262970912Sjulian LIST_FOREACH(type, &ng_typelist, types) { 263052419Sjulian num++; 263170912Sjulian } 263272200Sbmilekic mtx_unlock(&ng_typelist_mtx); 263352419Sjulian 263452419Sjulian /* Get response struct */ 2635229003Sglebius NG_MKRESPONSE(resp, msg, sizeof(*tl) + 2636229003Sglebius (num * sizeof(struct typeinfo)), M_NOWAIT); 263752419Sjulian if (resp == NULL) { 263852419Sjulian error = ENOMEM; 263952419Sjulian break; 264052419Sjulian } 264170700Sjulian tl = (struct typelist *) resp->data; 264252419Sjulian 264352419Sjulian /* Cycle through the linked list of types */ 264452419Sjulian tl->numtypes = 0; 264572200Sbmilekic mtx_lock(&ng_typelist_mtx); 264670700Sjulian LIST_FOREACH(type, &ng_typelist, types) { 264752419Sjulian struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 264852419Sjulian 264952419Sjulian if (tl->numtypes >= num) { 265052419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 265187599Sobrien __func__, "types"); 265252419Sjulian break; 265352419Sjulian } 2654125028Sharti strcpy(tp->type_name, type->name); 265571603Sjulian tp->numnodes = type->refs - 1; /* don't count list */ 265652419Sjulian tl->numtypes++; 265752419Sjulian } 265872200Sbmilekic mtx_unlock(&ng_typelist_mtx); 265952419Sjulian break; 266052419Sjulian } 266152419Sjulian 266253913Sarchie case NGM_BINARY2ASCII: 266353913Sarchie { 266464510Sarchie int bufSize = 20 * 1024; /* XXX hard coded constant */ 266553913Sarchie const struct ng_parse_type *argstype; 266653913Sarchie const struct ng_cmdlist *c; 266770700Sjulian struct ng_mesg *binary, *ascii; 266853913Sarchie 266953913Sarchie /* Data area must contain a valid netgraph message */ 267053913Sarchie binary = (struct ng_mesg *)msg->data; 2671152451Sglebius if (msg->header.arglen < sizeof(struct ng_mesg) || 2672152451Sglebius (msg->header.arglen - sizeof(struct ng_mesg) < 2673152451Sglebius binary->header.arglen)) { 267471047Sjulian TRAP_ERROR(); 267553913Sarchie error = EINVAL; 267653913Sarchie break; 267753913Sarchie } 267853913Sarchie 267970700Sjulian /* Get a response message with lots of room */ 268070700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 268170159Sjulian if (resp == NULL) { 268253913Sarchie error = ENOMEM; 268353913Sarchie break; 268453913Sarchie } 268570700Sjulian ascii = (struct ng_mesg *)resp->data; 268653913Sarchie 268753913Sarchie /* Copy binary message header to response message payload */ 268853913Sarchie bcopy(binary, ascii, sizeof(*binary)); 268953913Sarchie 269053913Sarchie /* Find command by matching typecookie and command number */ 2691229003Sglebius for (c = here->nd_type->cmdlist; c != NULL && c->name != NULL; 2692229003Sglebius c++) { 2693229003Sglebius if (binary->header.typecookie == c->cookie && 2694229003Sglebius binary->header.cmd == c->cmd) 269553913Sarchie break; 269653913Sarchie } 269753913Sarchie if (c == NULL || c->name == NULL) { 269853913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 2699229003Sglebius if (binary->header.typecookie == c->cookie && 2700229003Sglebius binary->header.cmd == c->cmd) 270153913Sarchie break; 270253913Sarchie } 270353913Sarchie if (c->name == NULL) { 270470700Sjulian NG_FREE_MSG(resp); 270553913Sarchie error = ENOSYS; 270653913Sarchie break; 270753913Sarchie } 270853913Sarchie } 270953913Sarchie 271053913Sarchie /* Convert command name to ASCII */ 271153913Sarchie snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 271253913Sarchie "%s", c->name); 271353913Sarchie 271453913Sarchie /* Convert command arguments to ASCII */ 271553913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 271653913Sarchie c->respType : c->mesgType; 271770912Sjulian if (argstype == NULL) { 271853913Sarchie *ascii->data = '\0'; 271970912Sjulian } else { 272053913Sarchie if ((error = ng_unparse(argstype, 272153913Sarchie (u_char *)binary->data, 272253913Sarchie ascii->data, bufSize)) != 0) { 272370700Sjulian NG_FREE_MSG(resp); 272453913Sarchie break; 272553913Sarchie } 272653913Sarchie } 272753913Sarchie 272853913Sarchie /* Return the result as struct ng_mesg plus ASCII string */ 272953913Sarchie bufSize = strlen(ascii->data) + 1; 273053913Sarchie ascii->header.arglen = bufSize; 273170700Sjulian resp->header.arglen = sizeof(*ascii) + bufSize; 273253913Sarchie break; 273353913Sarchie } 273453913Sarchie 273553913Sarchie case NGM_ASCII2BINARY: 273653913Sarchie { 2737208036Szec int bufSize = 20 * 1024; /* XXX hard coded constant */ 273853913Sarchie const struct ng_cmdlist *c; 273953913Sarchie const struct ng_parse_type *argstype; 274070700Sjulian struct ng_mesg *ascii, *binary; 274159178Sarchie int off = 0; 274253913Sarchie 274353913Sarchie /* Data area must contain at least a struct ng_mesg + '\0' */ 274453913Sarchie ascii = (struct ng_mesg *)msg->data; 2745152451Sglebius if ((msg->header.arglen < sizeof(*ascii) + 1) || 2746152451Sglebius (ascii->header.arglen < 1) || 2747152451Sglebius (msg->header.arglen < sizeof(*ascii) + 2748152451Sglebius ascii->header.arglen)) { 274971047Sjulian TRAP_ERROR(); 275053913Sarchie error = EINVAL; 275153913Sarchie break; 275253913Sarchie } 275353913Sarchie ascii->data[ascii->header.arglen - 1] = '\0'; 275453913Sarchie 275570700Sjulian /* Get a response message with lots of room */ 275670700Sjulian NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 275770159Sjulian if (resp == NULL) { 275853913Sarchie error = ENOMEM; 275953913Sarchie break; 276053913Sarchie } 276170700Sjulian binary = (struct ng_mesg *)resp->data; 276253913Sarchie 276353913Sarchie /* Copy ASCII message header to response message payload */ 276453913Sarchie bcopy(ascii, binary, sizeof(*ascii)); 276553913Sarchie 276653913Sarchie /* Find command by matching ASCII command string */ 276770784Sjulian for (c = here->nd_type->cmdlist; 276853913Sarchie c != NULL && c->name != NULL; c++) { 276953913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 277053913Sarchie break; 277153913Sarchie } 277253913Sarchie if (c == NULL || c->name == NULL) { 277353913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 277453913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 277553913Sarchie break; 277653913Sarchie } 277753913Sarchie if (c->name == NULL) { 277870700Sjulian NG_FREE_MSG(resp); 277953913Sarchie error = ENOSYS; 278053913Sarchie break; 278153913Sarchie } 278253913Sarchie } 278353913Sarchie 278453913Sarchie /* Convert command name to binary */ 278553913Sarchie binary->header.cmd = c->cmd; 278653913Sarchie binary->header.typecookie = c->cookie; 278753913Sarchie 278853913Sarchie /* Convert command arguments to binary */ 278953913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 279053913Sarchie c->respType : c->mesgType; 279170912Sjulian if (argstype == NULL) { 279253913Sarchie bufSize = 0; 279370912Sjulian } else { 2794229003Sglebius if ((error = ng_parse(argstype, ascii->data, &off, 2795229003Sglebius (u_char *)binary->data, &bufSize)) != 0) { 279670700Sjulian NG_FREE_MSG(resp); 279753913Sarchie break; 279853913Sarchie } 279953913Sarchie } 280053913Sarchie 280153913Sarchie /* Return the result */ 280253913Sarchie binary->header.arglen = bufSize; 280370700Sjulian resp->header.arglen = sizeof(*binary) + bufSize; 280453913Sarchie break; 280553913Sarchie } 280653913Sarchie 280762471Sphk case NGM_TEXT_CONFIG: 280852419Sjulian case NGM_TEXT_STATUS: 280952419Sjulian /* 281052419Sjulian * This one is tricky as it passes the command down to the 281152419Sjulian * actual node, even though it is a generic type command. 281270700Sjulian * This means we must assume that the item/msg is already freed 281352419Sjulian * when control passes back to us. 281452419Sjulian */ 281570784Sjulian if (here->nd_type->rcvmsg != NULL) { 281670700Sjulian NGI_MSG(item) = msg; /* put it back as we found it */ 281770784Sjulian return((*here->nd_type->rcvmsg)(here, item, lasthook)); 281852419Sjulian } 281952419Sjulian /* Fall through if rcvmsg not supported */ 282052419Sjulian default: 282171047Sjulian TRAP_ERROR(); 282252419Sjulian error = EINVAL; 282352419Sjulian } 282470700Sjulian /* 282570700Sjulian * Sometimes a generic message may be statically allocated 2826229003Sglebius * to avoid problems with allocating when in tight memory situations. 282770700Sjulian * Don't free it if it is so. 282870700Sjulian * I break them appart here, because erros may cause a free if the item 282970700Sjulian * in which case we'd be doing it twice. 283070700Sjulian * they are kept together above, to simplify freeing. 283170700Sjulian */ 283270700Sjulianout: 283370700Sjulian NG_RESPOND_MSG(error, here, item, resp); 2834185179Smav NG_FREE_MSG(msg); 283552419Sjulian return (error); 283652419Sjulian} 283752419Sjulian 283852419Sjulian/************************************************************************ 2839146213Sglebius Queue element get/free routines 2840146213Sglebius************************************************************************/ 2841146213Sglebius 2842146213Sglebiusuma_zone_t ng_qzone; 2843178259Smavuma_zone_t ng_qdzone; 2844186093Smavstatic int numthreads = 0; /* number of queue threads */ 2845176849Smavstatic int maxalloc = 4096;/* limit the damage of a leak */ 2846176849Smavstatic int maxdata = 512; /* limit the damage of a DoS */ 2847146213Sglebius 2848186093SmavTUNABLE_INT("net.graph.threads", &numthreads); 2849186093SmavSYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads, 2850186093Smav 0, "Number of queue processing threads"); 2851146213SglebiusTUNABLE_INT("net.graph.maxalloc", &maxalloc); 2852146213SglebiusSYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc, 2853178259Smav 0, "Maximum number of non-data queue items to allocate"); 2854176849SmavTUNABLE_INT("net.graph.maxdata", &maxdata); 2855178259SmavSYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata, 2856178259Smav 0, "Maximum number of data queue items to allocate"); 2857146213Sglebius 2858146213Sglebius#ifdef NETGRAPH_DEBUG 2859146213Sglebiusstatic TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist); 2860229003Sglebiusstatic int allocated; /* number of items malloc'd */ 2861146213Sglebius#endif 2862146213Sglebius 2863146213Sglebius/* 2864146213Sglebius * Get a queue entry. 2865146213Sglebius * This is usually called when a packet first enters netgraph. 2866146213Sglebius * By definition, this is usually from an interrupt, or from a user. 2867146213Sglebius * Users are not so important, but try be quick for the times that it's 2868146213Sglebius * an interrupt. 2869146213Sglebius */ 2870146213Sglebiusstatic __inline item_p 2871178259Smavng_alloc_item(int type, int flags) 2872146213Sglebius{ 2873178259Smav item_p item; 2874146213Sglebius 2875178259Smav KASSERT(((type & ~NGQF_TYPE) == 0), 2876178259Smav ("%s: incorrect item type: %d", __func__, type)); 2877146213Sglebius 2878229003Sglebius item = uma_zalloc((type == NGQF_DATA) ? ng_qdzone : ng_qzone, 2879178259Smav ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO); 2880146281Sglebius 2881178259Smav if (item) { 2882178259Smav item->el_flags = type; 2883146213Sglebius#ifdef NETGRAPH_DEBUG 2884178259Smav mtx_lock(&ngq_mtx); 2885178259Smav TAILQ_INSERT_TAIL(&ng_itemlist, item, all); 2886178259Smav allocated++; 2887178259Smav mtx_unlock(&ngq_mtx); 2888178259Smav#endif 2889146213Sglebius } 2890146213Sglebius 2891146213Sglebius return (item); 2892146213Sglebius} 2893146213Sglebius 2894146213Sglebius/* 2895146213Sglebius * Release a queue entry 2896146213Sglebius */ 2897146213Sglebiusvoid 2898146213Sglebiusng_free_item(item_p item) 2899146213Sglebius{ 2900146213Sglebius /* 2901146213Sglebius * The item may hold resources on it's own. We need to free 2902146213Sglebius * these before we can free the item. What they are depends upon 2903146213Sglebius * what kind of item it is. it is important that nodes zero 2904146213Sglebius * out pointers to resources that they remove from the item 2905146213Sglebius * or we release them again here. 2906146213Sglebius */ 2907146213Sglebius switch (item->el_flags & NGQF_TYPE) { 2908146213Sglebius case NGQF_DATA: 2909146213Sglebius /* If we have an mbuf still attached.. */ 2910146213Sglebius NG_FREE_M(_NGI_M(item)); 2911146213Sglebius break; 2912146213Sglebius case NGQF_MESG: 2913146213Sglebius _NGI_RETADDR(item) = 0; 2914146213Sglebius NG_FREE_MSG(_NGI_MSG(item)); 2915146213Sglebius break; 2916146213Sglebius case NGQF_FN: 2917172806Smav case NGQF_FN2: 2918146213Sglebius /* nothing to free really, */ 2919146213Sglebius _NGI_FN(item) = NULL; 2920146213Sglebius _NGI_ARG1(item) = NULL; 2921146213Sglebius _NGI_ARG2(item) = 0; 2922146213Sglebius break; 2923146213Sglebius } 2924146213Sglebius /* If we still have a node or hook referenced... */ 2925146213Sglebius _NGI_CLR_NODE(item); 2926146213Sglebius _NGI_CLR_HOOK(item); 2927146213Sglebius 2928146213Sglebius#ifdef NETGRAPH_DEBUG 2929146213Sglebius mtx_lock(&ngq_mtx); 2930146213Sglebius TAILQ_REMOVE(&ng_itemlist, item, all); 2931146213Sglebius allocated--; 2932146213Sglebius mtx_unlock(&ngq_mtx); 2933146213Sglebius#endif 2934229003Sglebius uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA) ? 2935229003Sglebius ng_qdzone : ng_qzone, item); 2936146213Sglebius} 2937146213Sglebius 2938178259Smav/* 2939178259Smav * Change type of the queue entry. 2940178259Smav * Possibly reallocates it from another UMA zone. 2941178259Smav */ 2942178259Smavstatic __inline item_p 2943178259Smavng_realloc_item(item_p pitem, int type, int flags) 2944178259Smav{ 2945178259Smav item_p item; 2946178259Smav int from, to; 2947178259Smav 2948178259Smav KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__)); 2949178259Smav KASSERT(((type & ~NGQF_TYPE) == 0), 2950178259Smav ("%s: incorrect item type: %d", __func__, type)); 2951178259Smav 2952178259Smav from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA); 2953178259Smav to = (type == NGQF_DATA); 2954178259Smav if (from != to) { 2955178259Smav /* If reallocation is required do it and copy item. */ 2956178259Smav if ((item = ng_alloc_item(type, flags)) == NULL) { 2957178259Smav ng_free_item(pitem); 2958178259Smav return (NULL); 2959178259Smav } 2960178259Smav *item = *pitem; 2961178259Smav ng_free_item(pitem); 2962178259Smav } else 2963178259Smav item = pitem; 2964178259Smav item->el_flags = (item->el_flags & ~NGQF_TYPE) | type; 2965178259Smav 2966178259Smav return (item); 2967178259Smav} 2968178259Smav 2969146213Sglebius/************************************************************************ 297052419Sjulian Module routines 297152419Sjulian************************************************************************/ 297252419Sjulian 297352419Sjulian/* 297452419Sjulian * Handle the loading/unloading of a netgraph node type module 297552419Sjulian */ 297652419Sjulianint 297752419Sjulianng_mod_event(module_t mod, int event, void *data) 297852419Sjulian{ 297952419Sjulian struct ng_type *const type = data; 2980229003Sglebius int error = 0; 298152419Sjulian 298252419Sjulian switch (event) { 298352419Sjulian case MOD_LOAD: 298452419Sjulian 298552419Sjulian /* Register new netgraph node type */ 2986229003Sglebius if ((error = ng_newtype(type)) != 0) 298752419Sjulian break; 298852419Sjulian 298952419Sjulian /* Call type specific code */ 299052419Sjulian if (type->mod_event != NULL) 299170700Sjulian if ((error = (*type->mod_event)(mod, event, data))) { 299272200Sbmilekic mtx_lock(&ng_typelist_mtx); 299371603Sjulian type->refs--; /* undo it */ 299452419Sjulian LIST_REMOVE(type, types); 299572200Sbmilekic mtx_unlock(&ng_typelist_mtx); 299670700Sjulian } 299752419Sjulian break; 299852419Sjulian 299952419Sjulian case MOD_UNLOAD: 300071603Sjulian if (type->refs > 1) { /* make sure no nodes exist! */ 300152419Sjulian error = EBUSY; 300271603Sjulian } else { 3003229003Sglebius if (type->refs == 0) /* failed load, nothing to undo */ 300471603Sjulian break; 300552419Sjulian if (type->mod_event != NULL) { /* check with type */ 300652419Sjulian error = (*type->mod_event)(mod, event, data); 3007229003Sglebius if (error != 0) /* type refuses.. */ 300852419Sjulian break; 300952419Sjulian } 301072200Sbmilekic mtx_lock(&ng_typelist_mtx); 301152419Sjulian LIST_REMOVE(type, types); 301272200Sbmilekic mtx_unlock(&ng_typelist_mtx); 301352419Sjulian } 301452419Sjulian break; 301552419Sjulian 301652419Sjulian default: 301752419Sjulian if (type->mod_event != NULL) 301852419Sjulian error = (*type->mod_event)(mod, event, data); 301952419Sjulian else 3020132199Sphk error = EOPNOTSUPP; /* XXX ? */ 302152419Sjulian break; 302252419Sjulian } 302352419Sjulian return (error); 302452419Sjulian} 302552419Sjulian 3026229003Sglebius#ifdef VIMAGE 3027195837Srwatsonstatic void 3028195837Srwatsonvnet_netgraph_uninit(const void *unused __unused) 3029193731Szec{ 3030207572Szec node_p node = NULL, last_killed = NULL; 3031207572Szec int i; 3032193731Szec 3033207572Szec do { 3034207572Szec /* Find a node to kill */ 3035207572Szec mtx_lock(&ng_namehash_mtx); 3036207572Szec for (i = 0; i < NG_NAME_HASH_SIZE; i++) { 3037207572Szec LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) { 3038207572Szec if (node != &ng_deadnode) { 3039207572Szec NG_NODE_REF(node); 3040207572Szec break; 3041207572Szec } 3042207572Szec } 3043207572Szec if (node != NULL) 3044207572Szec break; 3045207572Szec } 3046207572Szec mtx_unlock(&ng_namehash_mtx); 3047207572Szec 3048207572Szec /* Attempt to kill it only if it is a regular node */ 3049207572Szec if (node != NULL) { 3050207572Szec if (node == last_killed) { 3051207572Szec /* This should never happen */ 3052229003Sglebius printf("ng node %s needs NGF_REALLY_DIE\n", 3053229003Sglebius node->nd_name); 3054207572Szec if (node->nd_flags & NGF_REALLY_DIE) 3055207572Szec panic("ng node %s won't die", 3056207572Szec node->nd_name); 3057207572Szec node->nd_flags |= NGF_REALLY_DIE; 3058207572Szec } 3059193731Szec ng_rmnode(node, NULL, NULL, 0); 3060207572Szec NG_NODE_UNREF(node); 3061207572Szec last_killed = node; 3062193731Szec } 3063207572Szec } while (node != NULL); 3064193731Szec} 3065207572SzecVNET_SYSUNINIT(vnet_netgraph_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 3066195837Srwatson vnet_netgraph_uninit, NULL); 3067193731Szec#endif /* VIMAGE */ 3068193731Szec 306952419Sjulian/* 307052419Sjulian * Handle loading and unloading for this code. 307152419Sjulian * The only thing we need to link into is the NETISR strucure. 307252419Sjulian */ 307352419Sjulianstatic int 307452419Sjulianngb_mod_event(module_t mod, int event, void *data) 307552419Sjulian{ 3076186093Smav struct proc *p; 3077186093Smav struct thread *td; 3078186093Smav int i, error = 0; 307952419Sjulian 308052419Sjulian switch (event) { 308152419Sjulian case MOD_LOAD: 3082146212Sglebius /* Initialize everything. */ 3083168049Swkoszek NG_WORKLIST_LOCK_INIT(); 3084123278Struckman mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL, 3085123278Struckman MTX_DEF); 3086123278Struckman mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL, 3087123278Struckman MTX_DEF); 3088176802Smav mtx_init(&ng_namehash_mtx, "netgraph namehash mutex", NULL, 3089176802Smav MTX_DEF); 3090151974Sglebius mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL, 3091151974Sglebius MTX_DEF); 3092146212Sglebius#ifdef NETGRAPH_DEBUG 3093176802Smav mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL, 3094176802Smav MTX_DEF); 3095146212Sglebius mtx_init(&ngq_mtx, "netgraph item list mutex", NULL, 3096123278Struckman MTX_DEF); 3097146212Sglebius#endif 3098146212Sglebius ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item), 3099146212Sglebius NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0); 3100146212Sglebius uma_zone_set_max(ng_qzone, maxalloc); 3101229003Sglebius ng_qdzone = uma_zcreate("NetGraph data items", 3102229003Sglebius sizeof(struct ng_item), NULL, NULL, NULL, NULL, 3103229003Sglebius UMA_ALIGN_CACHE, 0); 3104178259Smav uma_zone_set_max(ng_qdzone, maxdata); 3105186093Smav /* Autoconfigure number of threads. */ 3106186093Smav if (numthreads <= 0) 3107186093Smav numthreads = mp_ncpus; 3108186093Smav /* Create threads. */ 3109186093Smav p = NULL; /* start with no process */ 3110186093Smav for (i = 0; i < numthreads; i++) { 3111186093Smav if (kproc_kthread_add(ngthread, NULL, &p, &td, 3112186093Smav RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) { 3113186093Smav numthreads = i; 3114186093Smav break; 3115186093Smav } 3116186093Smav } 311752419Sjulian break; 311852419Sjulian case MOD_UNLOAD: 3119167677Srwatson /* You can't unload it because an interface may be using it. */ 312052419Sjulian error = EBUSY; 312152419Sjulian break; 312252419Sjulian default: 312352419Sjulian error = EOPNOTSUPP; 312452419Sjulian break; 312552419Sjulian } 312652419Sjulian return (error); 312752419Sjulian} 312852419Sjulian 312952419Sjulianstatic moduledata_t netgraph_mod = { 313052419Sjulian "netgraph", 313152419Sjulian ngb_mod_event, 313252419Sjulian (NULL) 313352419Sjulian}; 3134139774SemaxDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE); 313572946SjulianSYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family"); 313672946SjulianSYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,""); 313772946SjulianSYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, ""); 313852419Sjulian 313970784Sjulian#ifdef NETGRAPH_DEBUG 314070700Sjulianvoid 314170784Sjuliandumphook (hook_p hook, char *file, int line) 314270784Sjulian{ 314370784Sjulian printf("hook: name %s, %d refs, Last touched:\n", 314470784Sjulian _NG_HOOK_NAME(hook), hook->hk_refs); 314570784Sjulian printf(" Last active @ %s, line %d\n", 314670784Sjulian hook->lastfile, hook->lastline); 314770784Sjulian if (line) { 314870784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 3149226829Sglebius#ifdef KDB 3150226829Sglebius kdb_backtrace(); 3151226829Sglebius#endif 315270784Sjulian } 315370784Sjulian} 315470784Sjulian 315570784Sjulianvoid 315670784Sjuliandumpnode(node_p node, char *file, int line) 315770784Sjulian{ 315870784Sjulian printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n", 315971047Sjulian _NG_NODE_ID(node), node->nd_type->name, 316070784Sjulian node->nd_numhooks, node->nd_flags, 316170784Sjulian node->nd_refs, node->nd_name); 316270784Sjulian printf(" Last active @ %s, line %d\n", 316370784Sjulian node->lastfile, node->lastline); 316470784Sjulian if (line) { 316570784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 3166226829Sglebius#ifdef KDB 3167226829Sglebius kdb_backtrace(); 3168226829Sglebius#endif 316970784Sjulian } 317070784Sjulian} 317170784Sjulian 317270784Sjulianvoid 317370700Sjuliandumpitem(item_p item, char *file, int line) 317470700Sjulian{ 3175146212Sglebius printf(" ACTIVE item, last used at %s, line %d", 3176146212Sglebius item->lastfile, item->lastline); 3177146212Sglebius switch(item->el_flags & NGQF_TYPE) { 3178146212Sglebius case NGQF_DATA: 3179146212Sglebius printf(" - [data]\n"); 3180146212Sglebius break; 3181146212Sglebius case NGQF_MESG: 3182146212Sglebius printf(" - retaddr[%d]:\n", _NGI_RETADDR(item)); 3183146212Sglebius break; 3184146212Sglebius case NGQF_FN: 3185172820Sru printf(" - fn@%p (%p, %p, %p, %d (%x))\n", 3186172820Sru _NGI_FN(item), 3187172820Sru _NGI_NODE(item), 3188172820Sru _NGI_HOOK(item), 3189172820Sru item->body.fn.fn_arg1, 3190172820Sru item->body.fn.fn_arg2, 3191172820Sru item->body.fn.fn_arg2); 3192172820Sru break; 3193172806Smav case NGQF_FN2: 3194173110Smav printf(" - fn2@%p (%p, %p, %p, %d (%x))\n", 3195172820Sru _NGI_FN2(item), 3196149735Sglebius _NGI_NODE(item), 3197149735Sglebius _NGI_HOOK(item), 3198146212Sglebius item->body.fn.fn_arg1, 3199146212Sglebius item->body.fn.fn_arg2, 3200146212Sglebius item->body.fn.fn_arg2); 3201146212Sglebius break; 320252419Sjulian } 320370784Sjulian if (line) { 320470784Sjulian printf(" problem discovered at file %s, line %d\n", file, line); 3205149735Sglebius if (_NGI_NODE(item)) { 320670784Sjulian printf("node %p ([%x])\n", 3207149735Sglebius _NGI_NODE(item), ng_node2ID(_NGI_NODE(item))); 320870784Sjulian } 320970784Sjulian } 321070700Sjulian} 321152419Sjulian 321270784Sjulianstatic void 321370784Sjulianng_dumpitems(void) 321470784Sjulian{ 321570784Sjulian item_p item; 321670784Sjulian int i = 1; 321770784Sjulian TAILQ_FOREACH(item, &ng_itemlist, all) { 321870784Sjulian printf("[%d] ", i++); 321970784Sjulian dumpitem(item, NULL, 0); 322070784Sjulian } 322170784Sjulian} 322270784Sjulian 322370784Sjulianstatic void 322470784Sjulianng_dumpnodes(void) 322570784Sjulian{ 322670784Sjulian node_p node; 322770784Sjulian int i = 1; 3228131008Srwatson mtx_lock(&ng_nodelist_mtx); 322970784Sjulian SLIST_FOREACH(node, &ng_allnodes, nd_all) { 323070784Sjulian printf("[%d] ", i++); 323170784Sjulian dumpnode(node, NULL, 0); 323270784Sjulian } 3233131008Srwatson mtx_unlock(&ng_nodelist_mtx); 323470784Sjulian} 323570784Sjulian 323670784Sjulianstatic void 323770784Sjulianng_dumphooks(void) 323870784Sjulian{ 323970784Sjulian hook_p hook; 324070784Sjulian int i = 1; 3241131008Srwatson mtx_lock(&ng_nodelist_mtx); 324270784Sjulian SLIST_FOREACH(hook, &ng_allhooks, hk_all) { 324370784Sjulian printf("[%d] ", i++); 324470784Sjulian dumphook(hook, NULL, 0); 324570784Sjulian } 3246131008Srwatson mtx_unlock(&ng_nodelist_mtx); 324770784Sjulian} 324870784Sjulian 324970700Sjulianstatic int 325070700Sjuliansysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS) 325170700Sjulian{ 325270700Sjulian int error; 325370700Sjulian int val; 325470700Sjulian int i; 325570700Sjulian 325670700Sjulian val = allocated; 325770700Sjulian i = 1; 3258170289Sdwmalone error = sysctl_handle_int(oidp, &val, 0, req); 325971047Sjulian if (error != 0 || req->newptr == NULL) 326071047Sjulian return (error); 326171047Sjulian if (val == 42) { 326270784Sjulian ng_dumpitems(); 326370784Sjulian ng_dumpnodes(); 326470784Sjulian ng_dumphooks(); 326570700Sjulian } 326671047Sjulian return (0); 326752419Sjulian} 326852419Sjulian 326971047SjulianSYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW, 327071047Sjulian 0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items"); 327170784Sjulian#endif /* NETGRAPH_DEBUG */ 327270700Sjulian 327370700Sjulian 327470700Sjulian/*********************************************************************** 327570700Sjulian* Worklist routines 327670700Sjulian**********************************************************************/ 327752419Sjulian/* 327870700Sjulian * Pick a node off the list of nodes with work, 3279186093Smav * try get an item to process off it. Remove the node from the list. 328052419Sjulian */ 328170700Sjulianstatic void 3282186093Smavngthread(void *arg) 328352419Sjulian{ 3284177953Smav for (;;) { 3285177953Smav node_p node; 328652419Sjulian 3287177953Smav /* Get node from the worklist. */ 3288168049Swkoszek NG_WORKLIST_LOCK(); 3289186093Smav while ((node = STAILQ_FIRST(&ng_worklist)) == NULL) 3290186093Smav NG_WORKLIST_SLEEP(); 3291178228Smav STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work); 3292168049Swkoszek NG_WORKLIST_UNLOCK(); 3293193731Szec CURVNET_SET(node->nd_vnet); 3294154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist", 3295154275Sglebius __func__, node->nd_ID, node); 329670700Sjulian /* 329770700Sjulian * We have the node. We also take over the reference 329870700Sjulian * that the list had on it. 329970700Sjulian * Now process as much as you can, until it won't 330070700Sjulian * let you have another item off the queue. 330170700Sjulian * All this time, keep the reference 330270700Sjulian * that lets us be sure that the node still exists. 330370700Sjulian * Let the reference go at the last minute. 330470700Sjulian */ 330570700Sjulian for (;;) { 3306177953Smav item_p item; 3307151238Sglebius int rw; 3308151238Sglebius 3309168049Swkoszek NG_QUEUE_LOCK(&node->nd_input_queue); 3310178228Smav item = ng_dequeue(node, &rw); 331170700Sjulian if (item == NULL) { 3312178228Smav node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ; 3313168049Swkoszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 331470700Sjulian break; /* go look for another node */ 331570700Sjulian } else { 3316168049Swkoszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 331774078Sjulian NGI_GET_NODE(item, node); /* zaps stored node */ 3318151238Sglebius ng_apply_item(node, item, rw); 331974078Sjulian NG_NODE_UNREF(node); 332070700Sjulian } 332170700Sjulian } 332273238Sjulian NG_NODE_UNREF(node); 3323193731Szec CURVNET_RESTORE(); 332452419Sjulian } 332570700Sjulian} 332669922Sjulian 332772979Sjulian/* 332872979Sjulian * XXX 332972979Sjulian * It's posible that a debugging NG_NODE_REF may need 333072979Sjulian * to be outside the mutex zone 333172979Sjulian */ 333270700Sjulianstatic void 3333177953Smavng_worklist_add(node_p node) 333470700Sjulian{ 3335148236Sglebius 3336148266Sglebius mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED); 3337148236Sglebius 3338178228Smav if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) { 333969922Sjulian /* 334070700Sjulian * If we are not already on the work queue, 334170700Sjulian * then put us on. 334269922Sjulian */ 3343178228Smav node->nd_input_queue.q_flags2 |= NGQ2_WORKQ; 3344229003Sglebius NG_NODE_REF(node); /* XXX safe in mutex? */ 3345168049Swkoszek NG_WORKLIST_LOCK(); 3346178228Smav STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work); 3347168049Swkoszek NG_WORKLIST_UNLOCK(); 3348154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__, 3349154275Sglebius node->nd_ID, node); 3350186093Smav NG_WORKLIST_WAKEUP(); 3351177953Smav } else { 3352154275Sglebius CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist", 3353154275Sglebius __func__, node->nd_ID, node); 3354177953Smav } 335570700Sjulian} 335670700Sjulian 335770700Sjulian 335870700Sjulian/*********************************************************************** 335970700Sjulian* Externally useable functions to set up a queue item ready for sending 336070700Sjulian***********************************************************************/ 336170700Sjulian 336270784Sjulian#ifdef NETGRAPH_DEBUG 336370784Sjulian#define ITEM_DEBUG_CHECKS \ 336470700Sjulian do { \ 336571849Sjulian if (NGI_NODE(item) ) { \ 336670700Sjulian printf("item already has node"); \ 3367174898Srwatson kdb_enter(KDB_WHY_NETGRAPH, "has node"); \ 336871849Sjulian NGI_CLR_NODE(item); \ 336970700Sjulian } \ 337071849Sjulian if (NGI_HOOK(item) ) { \ 337170700Sjulian printf("item already has hook"); \ 3372174898Srwatson kdb_enter(KDB_WHY_NETGRAPH, "has hook"); \ 337371849Sjulian NGI_CLR_HOOK(item); \ 337470700Sjulian } \ 337570700Sjulian } while (0) 337670700Sjulian#else 337770784Sjulian#define ITEM_DEBUG_CHECKS 337870700Sjulian#endif 337970700Sjulian 338070700Sjulian/* 3381131374Sjulian * Put mbuf into the item. 338270700Sjulian * Hook and node references will be removed when the item is dequeued. 338370700Sjulian * (or equivalent) 338470700Sjulian * (XXX) Unsafe because no reference held by peer on remote node. 338570700Sjulian * remote node might go away in this timescale. 338670700Sjulian * We know the hooks can't go away because that would require getting 338770700Sjulian * a writer item on both nodes and we must have at least a reader 3388151973Sglebius * here to be able to do this. 338970700Sjulian * Note that the hook loaded is the REMOTE hook. 339070700Sjulian * 339170700Sjulian * This is possibly in the critical path for new data. 339270700Sjulian */ 339370700Sjulianitem_p 3394146281Sglebiusng_package_data(struct mbuf *m, int flags) 339570700Sjulian{ 339670700Sjulian item_p item; 339770700Sjulian 3398178259Smav if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) { 3399176849Smav NG_FREE_M(m); 3400176849Smav return (NULL); 3401176849Smav } 340270784Sjulian ITEM_DEBUG_CHECKS; 3403178259Smav item->el_flags |= NGQF_READER; 340470700Sjulian NGI_M(item) = m; 340570700Sjulian return (item); 340670700Sjulian} 340770700Sjulian 340870700Sjulian/* 340970700Sjulian * Allocate a queue item and put items into it.. 341070700Sjulian * Evaluate the address as this will be needed to queue it and 341170700Sjulian * to work out what some of the fields should be. 341270700Sjulian * Hook and node references will be removed when the item is dequeued. 341370700Sjulian * (or equivalent) 341470700Sjulian */ 341570700Sjulianitem_p 3416146281Sglebiusng_package_msg(struct ng_mesg *msg, int flags) 341770700Sjulian{ 341870700Sjulian item_p item; 341970700Sjulian 3420178259Smav if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) { 342171849Sjulian NG_FREE_MSG(msg); 342270700Sjulian return (NULL); 342369922Sjulian } 342470784Sjulian ITEM_DEBUG_CHECKS; 3425149505Sglebius /* Messages items count as writers unless explicitly exempted. */ 3426149505Sglebius if (msg->header.cmd & NGM_READONLY) 3427178259Smav item->el_flags |= NGQF_READER; 3428149505Sglebius else 3429178259Smav item->el_flags |= NGQF_WRITER; 343070700Sjulian /* 343170700Sjulian * Set the current lasthook into the queue item 343270700Sjulian */ 343370700Sjulian NGI_MSG(item) = msg; 3434102244Sarchie NGI_RETADDR(item) = 0; 343570700Sjulian return (item); 343670700Sjulian} 343769922Sjulian 343870700Sjulian 343970700Sjulian 344071849Sjulian#define SET_RETADDR(item, here, retaddr) \ 344171047Sjulian do { /* Data or fn items don't have retaddrs */ \ 344271047Sjulian if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) { \ 344370700Sjulian if (retaddr) { \ 344470700Sjulian NGI_RETADDR(item) = retaddr; \ 344570700Sjulian } else { \ 344670700Sjulian /* \ 344770700Sjulian * The old return address should be ok. \ 344870700Sjulian * If there isn't one, use the address \ 344970700Sjulian * here. \ 345070700Sjulian */ \ 345170700Sjulian if (NGI_RETADDR(item) == 0) { \ 345270700Sjulian NGI_RETADDR(item) \ 345370700Sjulian = ng_node2ID(here); \ 345470700Sjulian } \ 345570700Sjulian } \ 345670700Sjulian } \ 345770700Sjulian } while (0) 345870700Sjulian 345970700Sjulianint 346070700Sjulianng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr) 346170700Sjulian{ 346271849Sjulian hook_p peer; 346371849Sjulian node_p peernode; 346470784Sjulian ITEM_DEBUG_CHECKS; 346570700Sjulian /* 346670700Sjulian * Quick sanity check.. 346770784Sjulian * Since a hook holds a reference on it's node, once we know 346870784Sjulian * that the peer is still connected (even if invalid,) we know 346970784Sjulian * that the peer node is present, though maybe invalid. 347070700Sjulian */ 3471219827Sglebius mtx_lock(&ng_topo_mtx); 3472229003Sglebius if ((hook == NULL) || NG_HOOK_NOT_VALID(hook) || 3473178311Smav NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) || 3474178311Smav NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) { 347570700Sjulian NG_FREE_ITEM(item); 347671047Sjulian TRAP_ERROR(); 3477219827Sglebius mtx_unlock(&ng_topo_mtx); 347873083Sjulian return (ENETDOWN); 347952419Sjulian } 348052419Sjulian 348170700Sjulian /* 348270700Sjulian * Transfer our interest to the other (peer) end. 348370700Sjulian */ 348471849Sjulian NG_HOOK_REF(peer); 3485178311Smav NG_NODE_REF(peernode); 348671849Sjulian NGI_SET_HOOK(item, peer); 348771849Sjulian NGI_SET_NODE(item, peernode); 348879706Sjulian SET_RETADDR(item, here, retaddr); 3489219827Sglebius 3490219827Sglebius mtx_unlock(&ng_topo_mtx); 3491219827Sglebius 349270700Sjulian return (0); 349370700Sjulian} 349452419Sjulian 349570700Sjulianint 3496227130Sfjoeng_address_path(node_p here, item_p item, const char *address, ng_ID_t retaddr) 349770700Sjulian{ 3498152451Sglebius node_p dest = NULL; 349970700Sjulian hook_p hook = NULL; 3500152451Sglebius int error; 350170700Sjulian 350270784Sjulian ITEM_DEBUG_CHECKS; 350370700Sjulian /* 350470700Sjulian * Note that ng_path2noderef increments the reference count 350570700Sjulian * on the node for us if it finds one. So we don't have to. 350670700Sjulian */ 350770700Sjulian error = ng_path2noderef(here, address, &dest, &hook); 350870700Sjulian if (error) { 350970700Sjulian NG_FREE_ITEM(item); 351070784Sjulian return (error); 351152419Sjulian } 351271849Sjulian NGI_SET_NODE(item, dest); 3513219827Sglebius if (hook) 351471849Sjulian NGI_SET_HOOK(item, hook); 3515219827Sglebius 351671849Sjulian SET_RETADDR(item, here, retaddr); 351770700Sjulian return (0); 351870700Sjulian} 351952419Sjulian 352070700Sjulianint 352170700Sjulianng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr) 352270700Sjulian{ 352370700Sjulian node_p dest; 352470700Sjulian 352570784Sjulian ITEM_DEBUG_CHECKS; 352670700Sjulian /* 352770700Sjulian * Find the target node. 352870700Sjulian */ 352970700Sjulian dest = ng_ID2noderef(ID); /* GETS REFERENCE! */ 353070700Sjulian if (dest == NULL) { 353170700Sjulian NG_FREE_ITEM(item); 353271047Sjulian TRAP_ERROR(); 353370700Sjulian return(EINVAL); 353470700Sjulian } 353570700Sjulian /* Fill out the contents */ 353671849Sjulian NGI_SET_NODE(item, dest); 353771849Sjulian NGI_CLR_HOOK(item); 353871849Sjulian SET_RETADDR(item, here, retaddr); 353952419Sjulian return (0); 354052419Sjulian} 354152419Sjulian 354252419Sjulian/* 354370700Sjulian * special case to send a message to self (e.g. destroy node) 354470700Sjulian * Possibly indicate an arrival hook too. 354570700Sjulian * Useful for removing that hook :-) 354652419Sjulian */ 354770700Sjulianitem_p 354870700Sjulianng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg) 354952419Sjulian{ 355070700Sjulian item_p item; 355152419Sjulian 355270700Sjulian /* 355370700Sjulian * Find the target node. 355470700Sjulian * If there is a HOOK argument, then use that in preference 355570700Sjulian * to the address. 355670700Sjulian */ 3557178259Smav if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) { 355871849Sjulian NG_FREE_MSG(msg); 355970700Sjulian return (NULL); 356052419Sjulian } 356170700Sjulian 356270700Sjulian /* Fill out the contents */ 3563178259Smav item->el_flags |= NGQF_WRITER; 356470784Sjulian NG_NODE_REF(here); 356571849Sjulian NGI_SET_NODE(item, here); 356671849Sjulian if (hook) { 356770784Sjulian NG_HOOK_REF(hook); 356871849Sjulian NGI_SET_HOOK(item, hook); 356971849Sjulian } 357070700Sjulian NGI_MSG(item) = msg; 357170700Sjulian NGI_RETADDR(item) = ng_node2ID(here); 357270700Sjulian return (item); 357352419Sjulian} 357452419Sjulian 3575172806Smav/* 3576172806Smav * Send ng_item_fn function call to the specified node. 3577172806Smav */ 3578172806Smav 3579146281Sglebiusint 3580173605Sglebiusng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2) 358171047Sjulian{ 358271047Sjulian 3583173605Sglebius return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS); 358471047Sjulian} 358571047Sjulian 3586172806Smavint 3587173605Sglebiusng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2, 3588172806Smav int flags) 3589172806Smav{ 3590172806Smav item_p item; 3591172806Smav 3592178259Smav if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) { 3593172806Smav return (ENOMEM); 3594172806Smav } 3595178259Smav item->el_flags |= NGQF_WRITER; 3596172806Smav NG_NODE_REF(node); /* and one for the item */ 3597172806Smav NGI_SET_NODE(item, node); 3598172806Smav if (hook) { 3599172806Smav NG_HOOK_REF(hook); 3600172806Smav NGI_SET_HOOK(item, hook); 3601172806Smav } 3602173605Sglebius NGI_FN(item) = fn; 3603172806Smav NGI_ARG1(item) = arg1; 3604172806Smav NGI_ARG2(item) = arg2; 3605172806Smav return(ng_snd_item(item, flags)); 3606172806Smav} 3607172806Smav 3608172806Smav/* 3609173605Sglebius * Send ng_item_fn2 function call to the specified node. 3610173605Sglebius * 3611173605Sglebius * If an optional pitem parameter is supplied, its apply 3612173605Sglebius * callback will be copied to the new item. If also NG_REUSE_ITEM 3613173605Sglebius * flag is set, no new item will be allocated, but pitem will 3614173605Sglebius * be used. 3615172806Smav */ 3616172806Smavint 3617173605Sglebiusng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1, 3618173605Sglebius int arg2, int flags) 3619172806Smav{ 3620172806Smav item_p item; 3621172806Smav 3622173605Sglebius KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0), 3623173605Sglebius ("%s: NG_REUSE_ITEM but no pitem", __func__)); 3624172806Smav 3625173605Sglebius /* 3626173605Sglebius * Allocate a new item if no supplied or 3627173605Sglebius * if we can't use supplied one. 3628173605Sglebius */ 3629173605Sglebius if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) { 3630178259Smav if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL) 3631173605Sglebius return (ENOMEM); 3632178259Smav if (pitem != NULL) 3633178259Smav item->apply = pitem->apply; 3634176849Smav } else { 3635178259Smav if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL) 3636178259Smav return (ENOMEM); 3637176849Smav } 3638172806Smav 3639178259Smav item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER; 3640172806Smav NG_NODE_REF(node); /* and one for the item */ 3641172806Smav NGI_SET_NODE(item, node); 3642172806Smav if (hook) { 3643172806Smav NG_HOOK_REF(hook); 3644172806Smav NGI_SET_HOOK(item, hook); 3645172806Smav } 3646172806Smav NGI_FN2(item) = fn; 3647172806Smav NGI_ARG1(item) = arg1; 3648172806Smav NGI_ARG2(item) = arg2; 3649172806Smav return(ng_snd_item(item, flags)); 3650172806Smav} 3651172806Smav 3652172806Smav/* 365391711Sjulian * Official timeout routines for Netgraph nodes. 365491711Sjulian */ 365591711Sjulianstatic void 3656140852Sglebiusng_callout_trampoline(void *arg) 365791711Sjulian{ 365891711Sjulian item_p item = arg; 365991711Sjulian 3660193731Szec CURVNET_SET(NGI_NODE(item)->nd_vnet); 366191711Sjulian ng_snd_item(item, 0); 3662193731Szec CURVNET_RESTORE(); 366391711Sjulian} 366491711Sjulian 366591711Sjulian 3666137138Sglebiusint 3667138268Sglebiusng_callout(struct callout *c, node_p node, hook_p hook, int ticks, 366891711Sjulian ng_item_fn *fn, void * arg1, int arg2) 366991711Sjulian{ 3670149881Sglebius item_p item, oitem; 367191711Sjulian 3672178259Smav if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL) 3673137138Sglebius return (ENOMEM); 3674137138Sglebius 3675178259Smav item->el_flags |= NGQF_WRITER; 367691711Sjulian NG_NODE_REF(node); /* and one for the item */ 367791711Sjulian NGI_SET_NODE(item, node); 367891711Sjulian if (hook) { 367991711Sjulian NG_HOOK_REF(hook); 368091711Sjulian NGI_SET_HOOK(item, hook); 368191711Sjulian } 368291711Sjulian NGI_FN(item) = fn; 368391711Sjulian NGI_ARG1(item) = arg1; 368491711Sjulian NGI_ARG2(item) = arg2; 3685149881Sglebius oitem = c->c_arg; 3686149881Sglebius if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 && 3687149881Sglebius oitem != NULL) 3688149881Sglebius NG_FREE_ITEM(oitem); 3689137138Sglebius return (0); 369091711Sjulian} 369191711Sjulian 369291711Sjulian/* A special modified version of untimeout() */ 3693152451Sglebiusint 3694138268Sglebiusng_uncallout(struct callout *c, node_p node) 369591711Sjulian{ 369691711Sjulian item_p item; 3697137138Sglebius int rval; 3698149357Sglebius 3699149357Sglebius KASSERT(c != NULL, ("ng_uncallout: NULL callout")); 3700149357Sglebius KASSERT(node != NULL, ("ng_uncallout: NULL node")); 3701149357Sglebius 3702137230Sglebius rval = callout_stop(c); 3703137138Sglebius item = c->c_arg; 3704137138Sglebius /* Do an extra check */ 3705140852Sglebius if ((rval > 0) && (c->c_func == &ng_callout_trampoline) && 3706137138Sglebius (NGI_NODE(item) == node)) { 370791711Sjulian /* 370891711Sjulian * We successfully removed it from the queue before it ran 3709152451Sglebius * So now we need to unreference everything that was 371091711Sjulian * given extra references. (NG_FREE_ITEM does this). 371191711Sjulian */ 371291711Sjulian NG_FREE_ITEM(item); 371391711Sjulian } 3714149881Sglebius c->c_arg = NULL; 3715137138Sglebius 3716137138Sglebius return (rval); 371791711Sjulian} 371891711Sjulian 371970700Sjulian/* 372070700Sjulian * Set the address, if none given, give the node here. 372170700Sjulian */ 372270700Sjulianvoid 372370700Sjulianng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr) 372470700Sjulian{ 372570700Sjulian if (retaddr) { 372670700Sjulian NGI_RETADDR(item) = retaddr; 372770700Sjulian } else { 372870700Sjulian /* 372970700Sjulian * The old return address should be ok. 373070700Sjulian * If there isn't one, use the address here. 373170700Sjulian */ 373270700Sjulian NGI_RETADDR(item) = ng_node2ID(here); 373370700Sjulian } 373470700Sjulian} 373552419Sjulian 373670700Sjulian#define TESTING 373770700Sjulian#ifdef TESTING 373870700Sjulian/* just test all the macros */ 373970700Sjulianvoid 374070700Sjulianng_macro_test(item_p item); 374170700Sjulianvoid 374270700Sjulianng_macro_test(item_p item) 374370700Sjulian{ 374470700Sjulian node_p node = NULL; 374570700Sjulian hook_p hook = NULL; 374670700Sjulian struct mbuf *m; 374770700Sjulian struct ng_mesg *msg; 374870700Sjulian ng_ID_t retaddr; 374970700Sjulian int error; 375070700Sjulian 375170700Sjulian NGI_GET_M(item, m); 375270700Sjulian NGI_GET_MSG(item, msg); 375370700Sjulian retaddr = NGI_RETADDR(item); 3754131374Sjulian NG_SEND_DATA(error, hook, m, NULL); 375570700Sjulian NG_SEND_DATA_ONLY(error, hook, m); 375670700Sjulian NG_FWD_NEW_DATA(error, item, hook, m); 375770784Sjulian NG_FWD_ITEM_HOOK(error, item, hook); 375870700Sjulian NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr); 375970700Sjulian NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr); 376070700Sjulian NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr); 376170700Sjulian NG_FWD_MSG_HOOK(error, node, item, hook, retaddr); 376270700Sjulian} 376370700Sjulian#endif /* TESTING */ 3764