14cf49a43SJulian Elischer /* 24cf49a43SJulian Elischer * ng_base.c 3c398230bSWarner Losh */ 4c398230bSWarner Losh 5c398230bSWarner Losh /*- 64cf49a43SJulian Elischer * Copyright (c) 1996-1999 Whistle Communications, Inc. 74cf49a43SJulian Elischer * All rights reserved. 84cf49a43SJulian Elischer * 94cf49a43SJulian Elischer * Subject to the following obligations and disclaimer of warranty, use and 104cf49a43SJulian Elischer * redistribution of this software, in source or object code forms, with or 114cf49a43SJulian Elischer * without modifications are expressly permitted by Whistle Communications; 124cf49a43SJulian Elischer * provided, however, that: 134cf49a43SJulian Elischer * 1. Any and all reproductions of the source or object code must include the 144cf49a43SJulian Elischer * copyright notice above and the following disclaimer of warranties; and 154cf49a43SJulian Elischer * 2. No rights are granted, in any manner or form, to use Whistle 164cf49a43SJulian Elischer * Communications, Inc. trademarks, including the mark "WHISTLE 174cf49a43SJulian Elischer * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 184cf49a43SJulian Elischer * such appears in the above copyright notice or in the software. 194cf49a43SJulian Elischer * 204cf49a43SJulian Elischer * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 214cf49a43SJulian Elischer * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 224cf49a43SJulian Elischer * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 234cf49a43SJulian Elischer * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 244cf49a43SJulian Elischer * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 254cf49a43SJulian Elischer * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 264cf49a43SJulian Elischer * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 274cf49a43SJulian Elischer * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 284cf49a43SJulian Elischer * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 294cf49a43SJulian Elischer * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 304cf49a43SJulian Elischer * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 314cf49a43SJulian Elischer * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 324cf49a43SJulian Elischer * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 334cf49a43SJulian Elischer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 344cf49a43SJulian Elischer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 354cf49a43SJulian Elischer * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 364cf49a43SJulian Elischer * OF SUCH DAMAGE. 374cf49a43SJulian Elischer * 38cc3bbd68SJulian Elischer * Authors: Julian Elischer <julian@freebsd.org> 39cc3bbd68SJulian Elischer * Archie Cobbs <archie@freebsd.org> 404cf49a43SJulian Elischer * 414cf49a43SJulian Elischer * $FreeBSD$ 424cf49a43SJulian Elischer * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $ 434cf49a43SJulian Elischer */ 444cf49a43SJulian Elischer 454cf49a43SJulian Elischer /* 464cf49a43SJulian Elischer * This file implements the base netgraph code. 474cf49a43SJulian Elischer */ 484cf49a43SJulian Elischer 494cf49a43SJulian Elischer #include <sys/param.h> 508e853a2cSGleb Smirnoff #include <sys/systm.h> 5177a58296SGleb Smirnoff #include <sys/ctype.h> 524cf49a43SJulian Elischer #include <sys/errno.h> 53f33ca0c9SMarcel Moolenaar #include <sys/kdb.h> 544cf49a43SJulian Elischer #include <sys/kernel.h> 553b33fbe7SGleb Smirnoff #include <sys/ktr.h> 56104a9b7eSAlexander Kabaev #include <sys/limits.h> 574cf49a43SJulian Elischer #include <sys/malloc.h> 584cf49a43SJulian Elischer #include <sys/mbuf.h> 5977a58296SGleb Smirnoff #include <sys/queue.h> 60bfa7e882SJulian Elischer #include <sys/sysctl.h> 6177a58296SGleb Smirnoff #include <sys/syslog.h> 62e088dd4cSAlexander Motin #include <sys/refcount.h> 6381a253a4SAlexander Motin #include <sys/proc.h> 64394cb30aSAlexander Motin #include <machine/cpu.h> 654cf49a43SJulian Elischer 664cf49a43SJulian Elischer #include <net/netisr.h> 674cf49a43SJulian Elischer 684cf49a43SJulian Elischer #include <netgraph/ng_message.h> 694cf49a43SJulian Elischer #include <netgraph/netgraph.h> 70f8307e12SArchie Cobbs #include <netgraph/ng_parse.h> 714cf49a43SJulian Elischer 729d72a7a3SJulian Elischer MODULE_VERSION(netgraph, NG_ABI_VERSION); 7399ff8176SPeter Wemm 74ac5dd141SGleb Smirnoff /* Mutex to protect topology events. */ 75ac5dd141SGleb Smirnoff static struct mtx ng_topo_mtx; 76ac5dd141SGleb Smirnoff 7730400f03SJulian Elischer #ifdef NETGRAPH_DEBUG 78cfea3f85SAlexander Motin static struct mtx ng_nodelist_mtx; /* protects global node/hook lists */ 791489164fSGleb Smirnoff static struct mtx ngq_mtx; /* protects the queue item list */ 8030400f03SJulian Elischer 8130400f03SJulian Elischer static SLIST_HEAD(, ng_node) ng_allnodes; 8230400f03SJulian Elischer static LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */ 8330400f03SJulian Elischer static SLIST_HEAD(, ng_hook) ng_allhooks; 8430400f03SJulian Elischer static LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */ 8530400f03SJulian Elischer 8630400f03SJulian Elischer static void ng_dumpitems(void); 8730400f03SJulian Elischer static void ng_dumpnodes(void); 8830400f03SJulian Elischer static void ng_dumphooks(void); 8930400f03SJulian Elischer 9030400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ 91954c4772SJulian Elischer /* 92954c4772SJulian Elischer * DEAD versions of the structures. 93954c4772SJulian Elischer * In order to avoid races, it is sometimes neccesary to point 94954c4772SJulian Elischer * at SOMETHING even though theoretically, the current entity is 95954c4772SJulian Elischer * INVALID. Use these to avoid these races. 96954c4772SJulian Elischer */ 97954c4772SJulian Elischer struct ng_type ng_deadtype = { 98954c4772SJulian Elischer NG_ABI_VERSION, 99954c4772SJulian Elischer "dead", 100954c4772SJulian Elischer NULL, /* modevent */ 101954c4772SJulian Elischer NULL, /* constructor */ 102954c4772SJulian Elischer NULL, /* rcvmsg */ 103954c4772SJulian Elischer NULL, /* shutdown */ 104954c4772SJulian Elischer NULL, /* newhook */ 105954c4772SJulian Elischer NULL, /* findhook */ 106954c4772SJulian Elischer NULL, /* connect */ 107954c4772SJulian Elischer NULL, /* rcvdata */ 108954c4772SJulian Elischer NULL, /* disconnect */ 109954c4772SJulian Elischer NULL, /* cmdlist */ 110954c4772SJulian Elischer }; 11130400f03SJulian Elischer 112954c4772SJulian Elischer struct ng_node ng_deadnode = { 113954c4772SJulian Elischer "dead", 114954c4772SJulian Elischer &ng_deadtype, 115be4252b3SJulian Elischer NGF_INVALID, 116954c4772SJulian Elischer 1, /* refs */ 117954c4772SJulian Elischer 0, /* numhooks */ 118954c4772SJulian Elischer NULL, /* private */ 119954c4772SJulian Elischer 0, /* ID */ 120954c4772SJulian Elischer LIST_HEAD_INITIALIZER(ng_deadnode.hooks), 121954c4772SJulian Elischer {}, /* all_nodes list entry */ 122954c4772SJulian Elischer {}, /* id hashtable list entry */ 123954c4772SJulian Elischer {}, /* workqueue entry */ 124954c4772SJulian Elischer { 0, 125954c4772SJulian Elischer {}, /* should never use! (should hang) */ 126954c4772SJulian Elischer NULL, 127954c4772SJulian Elischer &ng_deadnode.nd_input_queue.queue, 128954c4772SJulian Elischer &ng_deadnode 129954c4772SJulian Elischer }, 130954c4772SJulian Elischer #ifdef NETGRAPH_DEBUG 131954c4772SJulian Elischer ND_MAGIC, 132954c4772SJulian Elischer __FILE__, 133954c4772SJulian Elischer __LINE__, 134954c4772SJulian Elischer {NULL} 135954c4772SJulian Elischer #endif /* NETGRAPH_DEBUG */ 136954c4772SJulian Elischer }; 137954c4772SJulian Elischer 138954c4772SJulian Elischer struct ng_hook ng_deadhook = { 139954c4772SJulian Elischer "dead", 140954c4772SJulian Elischer NULL, /* private */ 141954c4772SJulian Elischer HK_INVALID | HK_DEAD, 142954c4772SJulian Elischer 1, /* refs always >= 1 */ 143e58d779dSGleb Smirnoff 0, /* undefined data link type */ 144954c4772SJulian Elischer &ng_deadhook, /* Peer is self */ 145954c4772SJulian Elischer &ng_deadnode, /* attached to deadnode */ 146954c4772SJulian Elischer {}, /* hooks list */ 147c4b5eea4SJulian Elischer NULL, /* override rcvmsg() */ 148c4b5eea4SJulian Elischer NULL, /* override rcvdata() */ 149954c4772SJulian Elischer #ifdef NETGRAPH_DEBUG 150954c4772SJulian Elischer HK_MAGIC, 151954c4772SJulian Elischer __FILE__, 152954c4772SJulian Elischer __LINE__, 153954c4772SJulian Elischer {NULL} 154954c4772SJulian Elischer #endif /* NETGRAPH_DEBUG */ 155954c4772SJulian Elischer }; 156954c4772SJulian Elischer 157954c4772SJulian Elischer /* 158954c4772SJulian Elischer * END DEAD STRUCTURES 159954c4772SJulian Elischer */ 160069154d5SJulian Elischer /* List nodes with unallocated work */ 161069154d5SJulian Elischer static TAILQ_HEAD(, ng_node) ng_worklist = TAILQ_HEAD_INITIALIZER(ng_worklist); 162b57a7965SJulian Elischer static struct mtx ng_worklist_mtx; /* MUST LOCK NODE FIRST */ 1634cf49a43SJulian Elischer 1644cf49a43SJulian Elischer /* List of installed types */ 165069154d5SJulian Elischer static LIST_HEAD(, ng_type) ng_typelist; 166069154d5SJulian Elischer static struct mtx ng_typelist_mtx; 1674cf49a43SJulian Elischer 168069154d5SJulian Elischer /* Hash related definitions */ 1690f150d04SJulian Elischer /* XXX Don't need to initialise them because it's a LIST */ 170cfea3f85SAlexander Motin #define NG_ID_HASH_SIZE 128 /* most systems wont need even this many */ 1710f150d04SJulian Elischer static LIST_HEAD(, ng_node) ng_ID_hash[NG_ID_HASH_SIZE]; 172069154d5SJulian Elischer static struct mtx ng_idhash_mtx; 1730f150d04SJulian Elischer /* Method to find a node.. used twice so do it here */ 1740f150d04SJulian Elischer #define NG_IDHASH_FN(ID) ((ID) % (NG_ID_HASH_SIZE)) 1750f150d04SJulian Elischer #define NG_IDHASH_FIND(ID, node) \ 1760f150d04SJulian Elischer do { \ 17753f9c5e9SRobert Watson mtx_assert(&ng_idhash_mtx, MA_OWNED); \ 1780f150d04SJulian Elischer LIST_FOREACH(node, &ng_ID_hash[NG_IDHASH_FN(ID)], \ 1790f150d04SJulian Elischer nd_idnodes) { \ 1800f150d04SJulian Elischer if (NG_NODE_IS_VALID(node) \ 1810f150d04SJulian Elischer && (NG_NODE_ID(node) == ID)) { \ 1820f150d04SJulian Elischer break; \ 1830f150d04SJulian Elischer } \ 1840f150d04SJulian Elischer } \ 1850f150d04SJulian Elischer } while (0) 186069154d5SJulian Elischer 187cfea3f85SAlexander Motin #define NG_NAME_HASH_SIZE 128 /* most systems wont need even this many */ 188cfea3f85SAlexander Motin static LIST_HEAD(, ng_node) ng_name_hash[NG_NAME_HASH_SIZE]; 189cfea3f85SAlexander Motin static struct mtx ng_namehash_mtx; 190cfea3f85SAlexander Motin #define NG_NAMEHASH(NAME, HASH) \ 191cfea3f85SAlexander Motin do { \ 192cfea3f85SAlexander Motin u_char h = 0; \ 193cfea3f85SAlexander Motin const u_char *c; \ 194cfea3f85SAlexander Motin for (c = (const u_char*)(NAME); *c; c++)\ 195cfea3f85SAlexander Motin h += *c; \ 196cfea3f85SAlexander Motin (HASH) = h % (NG_NAME_HASH_SIZE); \ 197cfea3f85SAlexander Motin } while (0) 198cfea3f85SAlexander Motin 199dc90cad9SJulian Elischer 2004cf49a43SJulian Elischer /* Internal functions */ 2014cf49a43SJulian Elischer static int ng_add_hook(node_p node, const char *name, hook_p * hookp); 202069154d5SJulian Elischer static int ng_generic_msg(node_p here, item_p item, hook_p lasthook); 203dc90cad9SJulian Elischer static ng_ID_t ng_decodeidname(const char *name); 2044cf49a43SJulian Elischer static int ngb_mod_event(module_t mod, int event, void *data); 205394cb30aSAlexander Motin static void ng_worklist_add(node_p node); 2064cf49a43SJulian Elischer static void ngintr(void); 20727757487SGleb Smirnoff static int ng_apply_item(node_p node, item_p item, int rw); 208069154d5SJulian Elischer static void ng_flush_input_queue(struct ng_queue * ngq); 209069154d5SJulian Elischer static node_p ng_ID2noderef(ng_ID_t ID); 210e088dd4cSAlexander Motin static int ng_con_nodes(item_p item, node_p node, const char *name, 211e088dd4cSAlexander Motin node_p node2, const char *name2); 212e088dd4cSAlexander Motin static int ng_con_part2(node_p node, item_p item, hook_p hook); 213e088dd4cSAlexander Motin static int ng_con_part3(node_p node, item_p item, hook_p hook); 2146b795970SJulian Elischer static int ng_mkpeer(node_p node, const char *name, 2156b795970SJulian Elischer const char *name2, char *type); 216069154d5SJulian Elischer 2174c9b5910SGleb Smirnoff /* Imported, these used to be externally visible, some may go back. */ 218069154d5SJulian Elischer void ng_destroy_hook(hook_p hook); 219069154d5SJulian Elischer node_p ng_name2noderef(node_p node, const char *name); 220069154d5SJulian Elischer int ng_path2noderef(node_p here, const char *path, 221069154d5SJulian Elischer node_p *dest, hook_p *lasthook); 222069154d5SJulian Elischer int ng_make_node(const char *type, node_p *nodepp); 223069154d5SJulian Elischer int ng_path_parse(char *addr, char **node, char **path, char **hook); 2241acb27c6SJulian Elischer void ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3); 22530400f03SJulian Elischer void ng_unname(node_p node); 226069154d5SJulian Elischer 2274cf49a43SJulian Elischer 2284cf49a43SJulian Elischer /* Our own netgraph malloc type */ 2294cf49a43SJulian Elischer MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); 230069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures"); 231069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures"); 232069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures"); 233069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage"); 234069154d5SJulian Elischer 235069154d5SJulian Elischer /* Should not be visible outside this file */ 23630400f03SJulian Elischer 23730400f03SJulian Elischer #define _NG_ALLOC_HOOK(hook) \ 23830400f03SJulian Elischer MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO) 23930400f03SJulian Elischer #define _NG_ALLOC_NODE(node) \ 24030400f03SJulian Elischer MALLOC(node, node_p, sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO) 24130400f03SJulian Elischer 2422c8dda8dSWojciech A. Koszek #define NG_QUEUE_LOCK_INIT(n) \ 2434abab3d5SWojciech A. Koszek mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF) 2442c8dda8dSWojciech A. Koszek #define NG_QUEUE_LOCK(n) \ 2454abab3d5SWojciech A. Koszek mtx_lock(&(n)->q_mtx) 2462c8dda8dSWojciech A. Koszek #define NG_QUEUE_UNLOCK(n) \ 2474abab3d5SWojciech A. Koszek mtx_unlock(&(n)->q_mtx) 2482c8dda8dSWojciech A. Koszek #define NG_WORKLIST_LOCK_INIT() \ 2494abab3d5SWojciech A. Koszek mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF) 2502c8dda8dSWojciech A. Koszek #define NG_WORKLIST_LOCK() \ 2514abab3d5SWojciech A. Koszek mtx_lock(&ng_worklist_mtx) 2522c8dda8dSWojciech A. Koszek #define NG_WORKLIST_UNLOCK() \ 2534abab3d5SWojciech A. Koszek mtx_unlock(&ng_worklist_mtx) 2542c8dda8dSWojciech A. Koszek 25530400f03SJulian Elischer #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ 25630400f03SJulian Elischer /* 25730400f03SJulian Elischer * In debug mode: 25830400f03SJulian Elischer * In an attempt to help track reference count screwups 25930400f03SJulian Elischer * we do not free objects back to the malloc system, but keep them 26030400f03SJulian Elischer * in a local cache where we can examine them and keep information safely 26130400f03SJulian Elischer * after they have been freed. 26230400f03SJulian Elischer * We use this scheme for nodes and hooks, and to some extent for items. 26330400f03SJulian Elischer */ 26430400f03SJulian Elischer static __inline hook_p 26530400f03SJulian Elischer ng_alloc_hook(void) 26630400f03SJulian Elischer { 26730400f03SJulian Elischer hook_p hook; 26830400f03SJulian Elischer SLIST_ENTRY(ng_hook) temp; 2699ed346baSBosko Milekic mtx_lock(&ng_nodelist_mtx); 27030400f03SJulian Elischer hook = LIST_FIRST(&ng_freehooks); 27130400f03SJulian Elischer if (hook) { 27230400f03SJulian Elischer LIST_REMOVE(hook, hk_hooks); 27330400f03SJulian Elischer bcopy(&hook->hk_all, &temp, sizeof(temp)); 27430400f03SJulian Elischer bzero(hook, sizeof(struct ng_hook)); 27530400f03SJulian Elischer bcopy(&temp, &hook->hk_all, sizeof(temp)); 2769ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); 27730400f03SJulian Elischer hook->hk_magic = HK_MAGIC; 27830400f03SJulian Elischer } else { 2799ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); 28030400f03SJulian Elischer _NG_ALLOC_HOOK(hook); 28130400f03SJulian Elischer if (hook) { 28230400f03SJulian Elischer hook->hk_magic = HK_MAGIC; 2839ed346baSBosko Milekic mtx_lock(&ng_nodelist_mtx); 28430400f03SJulian Elischer SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all); 2859ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); 28630400f03SJulian Elischer } 28730400f03SJulian Elischer } 28830400f03SJulian Elischer return (hook); 28930400f03SJulian Elischer } 29030400f03SJulian Elischer 29130400f03SJulian Elischer static __inline node_p 29230400f03SJulian Elischer ng_alloc_node(void) 29330400f03SJulian Elischer { 29430400f03SJulian Elischer node_p node; 29530400f03SJulian Elischer SLIST_ENTRY(ng_node) temp; 2969ed346baSBosko Milekic mtx_lock(&ng_nodelist_mtx); 29730400f03SJulian Elischer node = LIST_FIRST(&ng_freenodes); 29830400f03SJulian Elischer if (node) { 29930400f03SJulian Elischer LIST_REMOVE(node, nd_nodes); 30030400f03SJulian Elischer bcopy(&node->nd_all, &temp, sizeof(temp)); 30130400f03SJulian Elischer bzero(node, sizeof(struct ng_node)); 30230400f03SJulian Elischer bcopy(&temp, &node->nd_all, sizeof(temp)); 3039ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); 30430400f03SJulian Elischer node->nd_magic = ND_MAGIC; 30530400f03SJulian Elischer } else { 3069ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); 30730400f03SJulian Elischer _NG_ALLOC_NODE(node); 30830400f03SJulian Elischer if (node) { 30930400f03SJulian Elischer node->nd_magic = ND_MAGIC; 3109ed346baSBosko Milekic mtx_lock(&ng_nodelist_mtx); 31130400f03SJulian Elischer SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all); 3129ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); 31330400f03SJulian Elischer } 31430400f03SJulian Elischer } 31530400f03SJulian Elischer return (node); 31630400f03SJulian Elischer } 31730400f03SJulian Elischer 31830400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0) 31930400f03SJulian Elischer #define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0) 32030400f03SJulian Elischer 32130400f03SJulian Elischer 32230400f03SJulian Elischer #define NG_FREE_HOOK(hook) \ 32330400f03SJulian Elischer do { \ 3249ed346baSBosko Milekic mtx_lock(&ng_nodelist_mtx); \ 32530400f03SJulian Elischer LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks); \ 32630400f03SJulian Elischer hook->hk_magic = 0; \ 3279ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); \ 32830400f03SJulian Elischer } while (0) 32930400f03SJulian Elischer 33030400f03SJulian Elischer #define NG_FREE_NODE(node) \ 33130400f03SJulian Elischer do { \ 3329ed346baSBosko Milekic mtx_lock(&ng_nodelist_mtx); \ 33330400f03SJulian Elischer LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes); \ 33430400f03SJulian Elischer node->nd_magic = 0; \ 3359ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); \ 33630400f03SJulian Elischer } while (0) 33730400f03SJulian Elischer 33830400f03SJulian Elischer #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ 33930400f03SJulian Elischer 34030400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook) 34130400f03SJulian Elischer #define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node) 34230400f03SJulian Elischer 343069154d5SJulian Elischer #define NG_FREE_HOOK(hook) do { FREE((hook), M_NETGRAPH_HOOK); } while (0) 344069154d5SJulian Elischer #define NG_FREE_NODE(node) do { FREE((node), M_NETGRAPH_NODE); } while (0) 34530400f03SJulian Elischer 34630400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ 34730400f03SJulian Elischer 348f33ca0c9SMarcel Moolenaar /* Set this to kdb_enter("X") to catch all errors as they occur */ 3494cf49a43SJulian Elischer #ifndef TRAP_ERROR 3506b795970SJulian Elischer #define TRAP_ERROR() 3514cf49a43SJulian Elischer #endif 3524cf49a43SJulian Elischer 353dc90cad9SJulian Elischer static ng_ID_t nextID = 1; 354dc90cad9SJulian Elischer 355b2da83c2SArchie Cobbs #ifdef INVARIANTS 356b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m) do { \ 357b2da83c2SArchie Cobbs struct mbuf *n; \ 358b2da83c2SArchie Cobbs int total; \ 359b2da83c2SArchie Cobbs \ 360fe584538SDag-Erling Smørgrav M_ASSERTPKTHDR(m); \ 361b32cfb32SGleb Smirnoff for (total = 0, n = (m); n != NULL; n = n->m_next) { \ 362b2da83c2SArchie Cobbs total += n->m_len; \ 363b32cfb32SGleb Smirnoff if (n->m_nextpkt != NULL) \ 364b32cfb32SGleb Smirnoff panic("%s: m_nextpkt", __func__); \ 365b32cfb32SGleb Smirnoff } \ 366ba5b359aSGleb Smirnoff \ 367b2da83c2SArchie Cobbs if ((m)->m_pkthdr.len != total) { \ 368b2da83c2SArchie Cobbs panic("%s: %d != %d", \ 3696e551fb6SDavid E. O'Brien __func__, (m)->m_pkthdr.len, total); \ 370b2da83c2SArchie Cobbs } \ 371b2da83c2SArchie Cobbs } while (0) 372b2da83c2SArchie Cobbs #else 373b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m) 374b2da83c2SArchie Cobbs #endif 375b2da83c2SArchie Cobbs 376e088dd4cSAlexander Motin #define ERROUT(x) do { error = (x); goto done; } while (0) 377dc90cad9SJulian Elischer 3784cf49a43SJulian Elischer /************************************************************************ 379f8307e12SArchie Cobbs Parse type definitions for generic messages 380f8307e12SArchie Cobbs ************************************************************************/ 381f8307e12SArchie Cobbs 382f8307e12SArchie Cobbs /* Handy structure parse type defining macro */ 383f8307e12SArchie Cobbs #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ 384f0184ff8SArchie Cobbs static const struct ng_parse_struct_field \ 385f0184ff8SArchie Cobbs ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args; \ 386f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_ ## lo ## _type = { \ 387f8307e12SArchie Cobbs &ng_parse_struct_type, \ 388f0184ff8SArchie Cobbs &ng_ ## lo ## _type_fields \ 389f8307e12SArchie Cobbs } 390f8307e12SArchie Cobbs 391f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); 392f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); 393f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); 394f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); 395f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); 396f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); 397f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); 398f8307e12SArchie Cobbs 399f8307e12SArchie Cobbs /* Get length of an array when the length is stored as a 32 bit 400d7d97eb0SJeroen Ruigrok van der Werven value immediately preceding the array -- as with struct namelist 401f8307e12SArchie Cobbs and struct typelist. */ 402f8307e12SArchie Cobbs static int 403f8307e12SArchie Cobbs ng_generic_list_getLength(const struct ng_parse_type *type, 404f8307e12SArchie Cobbs const u_char *start, const u_char *buf) 405f8307e12SArchie Cobbs { 406f8307e12SArchie Cobbs return *((const u_int32_t *)(buf - 4)); 407f8307e12SArchie Cobbs } 408f8307e12SArchie Cobbs 409f8307e12SArchie Cobbs /* Get length of the array of struct linkinfo inside a struct hooklist */ 410f8307e12SArchie Cobbs static int 411f8307e12SArchie Cobbs ng_generic_linkinfo_getLength(const struct ng_parse_type *type, 412f8307e12SArchie Cobbs const u_char *start, const u_char *buf) 413f8307e12SArchie Cobbs { 414f8307e12SArchie Cobbs const struct hooklist *hl = (const struct hooklist *)start; 415f8307e12SArchie Cobbs 416f8307e12SArchie Cobbs return hl->nodeinfo.hooks; 417f8307e12SArchie Cobbs } 418f8307e12SArchie Cobbs 419f8307e12SArchie Cobbs /* Array type for a variable length array of struct namelist */ 420f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_nodeinfoarray_type_info = { 421f8307e12SArchie Cobbs &ng_generic_nodeinfo_type, 422f8307e12SArchie Cobbs &ng_generic_list_getLength 423f8307e12SArchie Cobbs }; 424f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_nodeinfoarray_type = { 425f8307e12SArchie Cobbs &ng_parse_array_type, 426f8307e12SArchie Cobbs &ng_nodeinfoarray_type_info 427f8307e12SArchie Cobbs }; 428f8307e12SArchie Cobbs 429f8307e12SArchie Cobbs /* Array type for a variable length array of struct typelist */ 430f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_typeinfoarray_type_info = { 431f8307e12SArchie Cobbs &ng_generic_typeinfo_type, 432f8307e12SArchie Cobbs &ng_generic_list_getLength 433f8307e12SArchie Cobbs }; 434f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_typeinfoarray_type = { 435f8307e12SArchie Cobbs &ng_parse_array_type, 436f8307e12SArchie Cobbs &ng_typeinfoarray_type_info 437f8307e12SArchie Cobbs }; 438f8307e12SArchie Cobbs 439f8307e12SArchie Cobbs /* Array type for array of struct linkinfo in struct hooklist */ 440f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { 441f8307e12SArchie Cobbs &ng_generic_linkinfo_type, 442f8307e12SArchie Cobbs &ng_generic_linkinfo_getLength 443f8307e12SArchie Cobbs }; 444f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_linkinfo_array_type = { 445f8307e12SArchie Cobbs &ng_parse_array_type, 446f8307e12SArchie Cobbs &ng_generic_linkinfo_array_type_info 447f8307e12SArchie Cobbs }; 448f8307e12SArchie Cobbs 449f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); 450f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, 451f8307e12SArchie Cobbs (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); 452f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, 453f8307e12SArchie Cobbs (&ng_generic_nodeinfoarray_type)); 454f8307e12SArchie Cobbs 455f8307e12SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 456f8307e12SArchie Cobbs static const struct ng_cmdlist ng_generic_cmds[] = { 457f8307e12SArchie Cobbs { 458f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 459f8307e12SArchie Cobbs NGM_SHUTDOWN, 460f8307e12SArchie Cobbs "shutdown", 461f8307e12SArchie Cobbs NULL, 462f8307e12SArchie Cobbs NULL 463f8307e12SArchie Cobbs }, 464f8307e12SArchie Cobbs { 465f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 466f8307e12SArchie Cobbs NGM_MKPEER, 467f8307e12SArchie Cobbs "mkpeer", 468f8307e12SArchie Cobbs &ng_generic_mkpeer_type, 469f8307e12SArchie Cobbs NULL 470f8307e12SArchie Cobbs }, 471f8307e12SArchie Cobbs { 472f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 473f8307e12SArchie Cobbs NGM_CONNECT, 474f8307e12SArchie Cobbs "connect", 475f8307e12SArchie Cobbs &ng_generic_connect_type, 476f8307e12SArchie Cobbs NULL 477f8307e12SArchie Cobbs }, 478f8307e12SArchie Cobbs { 479f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 480f8307e12SArchie Cobbs NGM_NAME, 481f8307e12SArchie Cobbs "name", 482f8307e12SArchie Cobbs &ng_generic_name_type, 483f8307e12SArchie Cobbs NULL 484f8307e12SArchie Cobbs }, 485f8307e12SArchie Cobbs { 486f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 487f8307e12SArchie Cobbs NGM_RMHOOK, 488f8307e12SArchie Cobbs "rmhook", 489f8307e12SArchie Cobbs &ng_generic_rmhook_type, 490f8307e12SArchie Cobbs NULL 491f8307e12SArchie Cobbs }, 492f8307e12SArchie Cobbs { 493f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 494f8307e12SArchie Cobbs NGM_NODEINFO, 495f8307e12SArchie Cobbs "nodeinfo", 496f8307e12SArchie Cobbs NULL, 497f8307e12SArchie Cobbs &ng_generic_nodeinfo_type 498f8307e12SArchie Cobbs }, 499f8307e12SArchie Cobbs { 500f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 501f8307e12SArchie Cobbs NGM_LISTHOOKS, 502f8307e12SArchie Cobbs "listhooks", 503f8307e12SArchie Cobbs NULL, 504f8307e12SArchie Cobbs &ng_generic_hooklist_type 505f8307e12SArchie Cobbs }, 506f8307e12SArchie Cobbs { 507f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 508f8307e12SArchie Cobbs NGM_LISTNAMES, 509f8307e12SArchie Cobbs "listnames", 510f8307e12SArchie Cobbs NULL, 511f8307e12SArchie Cobbs &ng_generic_listnodes_type /* same as NGM_LISTNODES */ 512f8307e12SArchie Cobbs }, 513f8307e12SArchie Cobbs { 514f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 515f8307e12SArchie Cobbs NGM_LISTNODES, 516f8307e12SArchie Cobbs "listnodes", 517f8307e12SArchie Cobbs NULL, 518f8307e12SArchie Cobbs &ng_generic_listnodes_type 519f8307e12SArchie Cobbs }, 520f8307e12SArchie Cobbs { 521f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 522f8307e12SArchie Cobbs NGM_LISTTYPES, 523f8307e12SArchie Cobbs "listtypes", 524f8307e12SArchie Cobbs NULL, 525f8307e12SArchie Cobbs &ng_generic_typeinfo_type 526f8307e12SArchie Cobbs }, 527f8307e12SArchie Cobbs { 528f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 5297095e097SPoul-Henning Kamp NGM_TEXT_CONFIG, 5307095e097SPoul-Henning Kamp "textconfig", 5317095e097SPoul-Henning Kamp NULL, 5327095e097SPoul-Henning Kamp &ng_parse_string_type 5337095e097SPoul-Henning Kamp }, 5347095e097SPoul-Henning Kamp { 5357095e097SPoul-Henning Kamp NGM_GENERIC_COOKIE, 536f8307e12SArchie Cobbs NGM_TEXT_STATUS, 537f8307e12SArchie Cobbs "textstatus", 538f8307e12SArchie Cobbs NULL, 539f8307e12SArchie Cobbs &ng_parse_string_type 540f8307e12SArchie Cobbs }, 541f8307e12SArchie Cobbs { 542f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 543f8307e12SArchie Cobbs NGM_ASCII2BINARY, 544f8307e12SArchie Cobbs "ascii2binary", 545f8307e12SArchie Cobbs &ng_parse_ng_mesg_type, 546f8307e12SArchie Cobbs &ng_parse_ng_mesg_type 547f8307e12SArchie Cobbs }, 548f8307e12SArchie Cobbs { 549f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 550f8307e12SArchie Cobbs NGM_BINARY2ASCII, 551f8307e12SArchie Cobbs "binary2ascii", 552f8307e12SArchie Cobbs &ng_parse_ng_mesg_type, 553f8307e12SArchie Cobbs &ng_parse_ng_mesg_type 554f8307e12SArchie Cobbs }, 555f8307e12SArchie Cobbs { 0 } 556f8307e12SArchie Cobbs }; 557f8307e12SArchie Cobbs 558f8307e12SArchie Cobbs /************************************************************************ 5594cf49a43SJulian Elischer Node routines 5604cf49a43SJulian Elischer ************************************************************************/ 5614cf49a43SJulian Elischer 5624cf49a43SJulian Elischer /* 5634cf49a43SJulian Elischer * Instantiate a node of the requested type 5644cf49a43SJulian Elischer */ 5654cf49a43SJulian Elischer int 5664cf49a43SJulian Elischer ng_make_node(const char *typename, node_p *nodepp) 5674cf49a43SJulian Elischer { 5684cf49a43SJulian Elischer struct ng_type *type; 569069154d5SJulian Elischer int error; 5704cf49a43SJulian Elischer 5714cf49a43SJulian Elischer /* Check that the type makes sense */ 5724cf49a43SJulian Elischer if (typename == NULL) { 5736b795970SJulian Elischer TRAP_ERROR(); 5744cf49a43SJulian Elischer return (EINVAL); 5754cf49a43SJulian Elischer } 5764cf49a43SJulian Elischer 5777610f574SGleb Smirnoff /* Locate the node type. If we fail we return. Do not try to load 5787610f574SGleb Smirnoff * module. 5797610f574SGleb Smirnoff */ 5804cf49a43SJulian Elischer if ((type = ng_findtype(typename)) == NULL) 5814cf49a43SJulian Elischer return (ENXIO); 5824cf49a43SJulian Elischer 583069154d5SJulian Elischer /* 584069154d5SJulian Elischer * If we have a constructor, then make the node and 585069154d5SJulian Elischer * call the constructor to do type specific initialisation. 586069154d5SJulian Elischer */ 587069154d5SJulian Elischer if (type->constructor != NULL) { 588069154d5SJulian Elischer if ((error = ng_make_node_common(type, nodepp)) == 0) { 589069154d5SJulian Elischer if ((error = ((*type->constructor)(*nodepp)) != 0)) { 59030400f03SJulian Elischer NG_NODE_UNREF(*nodepp); 591069154d5SJulian Elischer } 592069154d5SJulian Elischer } 593069154d5SJulian Elischer } else { 594069154d5SJulian Elischer /* 595069154d5SJulian Elischer * Node has no constructor. We cannot ask for one 59664efc707SRobert Watson * to be made. It must be brought into existence by 597954c4772SJulian Elischer * some external agency. The external agency should 598069154d5SJulian Elischer * call ng_make_node_common() directly to get the 599069154d5SJulian Elischer * netgraph part initialised. 600069154d5SJulian Elischer */ 6016b795970SJulian Elischer TRAP_ERROR(); 602069154d5SJulian Elischer error = EINVAL; 603069154d5SJulian Elischer } 604069154d5SJulian Elischer return (error); 6054cf49a43SJulian Elischer } 6064cf49a43SJulian Elischer 6074cf49a43SJulian Elischer /* 608069154d5SJulian Elischer * Generic node creation. Called by node initialisation for externally 609069154d5SJulian Elischer * instantiated nodes (e.g. hardware, sockets, etc ). 6104cf49a43SJulian Elischer * The returned node has a reference count of 1. 6114cf49a43SJulian Elischer */ 6124cf49a43SJulian Elischer int 6134cf49a43SJulian Elischer ng_make_node_common(struct ng_type *type, node_p *nodepp) 6144cf49a43SJulian Elischer { 6154cf49a43SJulian Elischer node_p node; 6164cf49a43SJulian Elischer 6174cf49a43SJulian Elischer /* Require the node type to have been already installed */ 6184cf49a43SJulian Elischer if (ng_findtype(type->name) == NULL) { 6196b795970SJulian Elischer TRAP_ERROR(); 6204cf49a43SJulian Elischer return (EINVAL); 6214cf49a43SJulian Elischer } 6224cf49a43SJulian Elischer 6234cf49a43SJulian Elischer /* Make a node and try attach it to the type */ 62430400f03SJulian Elischer NG_ALLOC_NODE(node); 6254cf49a43SJulian Elischer if (node == NULL) { 6266b795970SJulian Elischer TRAP_ERROR(); 6274cf49a43SJulian Elischer return (ENOMEM); 6284cf49a43SJulian Elischer } 62930400f03SJulian Elischer node->nd_type = type; 63030400f03SJulian Elischer NG_NODE_REF(node); /* note reference */ 6314cf49a43SJulian Elischer type->refs++; 6324cf49a43SJulian Elischer 6332c8dda8dSWojciech A. Koszek NG_QUEUE_LOCK_INIT(&node->nd_input_queue); 63430400f03SJulian Elischer node->nd_input_queue.queue = NULL; 63530400f03SJulian Elischer node->nd_input_queue.last = &node->nd_input_queue.queue; 63630400f03SJulian Elischer node->nd_input_queue.q_flags = 0; 63730400f03SJulian Elischer node->nd_input_queue.q_node = node; 6384cf49a43SJulian Elischer 6394cf49a43SJulian Elischer /* Initialize hook list for new node */ 64030400f03SJulian Elischer LIST_INIT(&node->nd_hooks); 6414cf49a43SJulian Elischer 642cfea3f85SAlexander Motin /* Link us into the name hash. */ 643cfea3f85SAlexander Motin mtx_lock(&ng_namehash_mtx); 644cfea3f85SAlexander Motin LIST_INSERT_HEAD(&ng_name_hash[0], node, nd_nodes); 645cfea3f85SAlexander Motin mtx_unlock(&ng_namehash_mtx); 646069154d5SJulian Elischer 647dc90cad9SJulian Elischer /* get an ID and put us in the hash chain */ 6489ed346baSBosko Milekic mtx_lock(&ng_idhash_mtx); 64930400f03SJulian Elischer for (;;) { /* wrap protection, even if silly */ 650069154d5SJulian Elischer node_p node2 = NULL; 65130400f03SJulian Elischer node->nd_ID = nextID++; /* 137/second for 1 year before wrap */ 6520f150d04SJulian Elischer 65330400f03SJulian Elischer /* Is there a problem with the new number? */ 6540f150d04SJulian Elischer NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */ 6550f150d04SJulian Elischer if ((node->nd_ID != 0) && (node2 == NULL)) { 65630400f03SJulian Elischer break; 657069154d5SJulian Elischer } 65830400f03SJulian Elischer } 6590f150d04SJulian Elischer LIST_INSERT_HEAD(&ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], 66030400f03SJulian Elischer node, nd_idnodes); 6619ed346baSBosko Milekic mtx_unlock(&ng_idhash_mtx); 662dc90cad9SJulian Elischer 6634cf49a43SJulian Elischer /* Done */ 6644cf49a43SJulian Elischer *nodepp = node; 6654cf49a43SJulian Elischer return (0); 6664cf49a43SJulian Elischer } 6674cf49a43SJulian Elischer 6684cf49a43SJulian Elischer /* 6694cf49a43SJulian Elischer * Forceably start the shutdown process on a node. Either call 67064efc707SRobert Watson * its shutdown method, or do the default shutdown if there is 6714cf49a43SJulian Elischer * no type-specific method. 6724cf49a43SJulian Elischer * 67364efc707SRobert Watson * We can only be called from a shutdown message, so we know we have 6743e4084c8SJulian Elischer * a writer lock, and therefore exclusive access. It also means 6753e4084c8SJulian Elischer * that we should not be on the work queue, but we check anyhow. 676069154d5SJulian Elischer * 677069154d5SJulian Elischer * Persistent node types must have a type-specific method which 67864efc707SRobert Watson * allocates a new node in which case, this one is irretrievably going away, 6793e4084c8SJulian Elischer * or cleans up anything it needs, and just makes the node valid again, 6803e4084c8SJulian Elischer * in which case we allow the node to survive. 6813e4084c8SJulian Elischer * 68264efc707SRobert Watson * XXX We need to think of how to tell a persistent node that we 6833e4084c8SJulian Elischer * REALLY need to go away because the hardware has gone or we 6843e4084c8SJulian Elischer * are rebooting.... etc. 6854cf49a43SJulian Elischer */ 6864cf49a43SJulian Elischer void 6871acb27c6SJulian Elischer ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3) 6884cf49a43SJulian Elischer { 6893e4084c8SJulian Elischer hook_p hook; 6903e4084c8SJulian Elischer 6914cf49a43SJulian Elischer /* Check if it's already shutting down */ 692be4252b3SJulian Elischer if ((node->nd_flags & NGF_CLOSING) != 0) 6934cf49a43SJulian Elischer return; 6944cf49a43SJulian Elischer 6951acb27c6SJulian Elischer if (node == &ng_deadnode) { 6961acb27c6SJulian Elischer printf ("shutdown called on deadnode\n"); 6971acb27c6SJulian Elischer return; 6981acb27c6SJulian Elischer } 6991acb27c6SJulian Elischer 7004cf49a43SJulian Elischer /* Add an extra reference so it doesn't go away during this */ 70130400f03SJulian Elischer NG_NODE_REF(node); 7024cf49a43SJulian Elischer 70330400f03SJulian Elischer /* 70430400f03SJulian Elischer * Mark it invalid so any newcomers know not to try use it 70530400f03SJulian Elischer * Also add our own mark so we can't recurse 706be4252b3SJulian Elischer * note that NGF_INVALID does not do this as it's also set during 70730400f03SJulian Elischer * creation 70830400f03SJulian Elischer */ 709be4252b3SJulian Elischer node->nd_flags |= NGF_INVALID|NGF_CLOSING; 7104cf49a43SJulian Elischer 711991fc65aSJulian Elischer /* If node has its pre-shutdown method, then call it first*/ 712991fc65aSJulian Elischer if (node->nd_type && node->nd_type->close) 713991fc65aSJulian Elischer (*node->nd_type->close)(node); 714991fc65aSJulian Elischer 7153e4084c8SJulian Elischer /* Notify all remaining connected nodes to disconnect */ 7163e4084c8SJulian Elischer while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL) 7173e4084c8SJulian Elischer ng_destroy_hook(hook); 71830400f03SJulian Elischer 719069154d5SJulian Elischer /* 720069154d5SJulian Elischer * Drain the input queue forceably. 72130400f03SJulian Elischer * it has no hooks so what's it going to do, bleed on someone? 72230400f03SJulian Elischer * Theoretically we came here from a queue entry that was added 72330400f03SJulian Elischer * Just before the queue was closed, so it should be empty anyway. 724b57a7965SJulian Elischer * Also removes us from worklist if needed. 725069154d5SJulian Elischer */ 72630400f03SJulian Elischer ng_flush_input_queue(&node->nd_input_queue); 727069154d5SJulian Elischer 728069154d5SJulian Elischer /* Ask the type if it has anything to do in this case */ 72930400f03SJulian Elischer if (node->nd_type && node->nd_type->shutdown) { 73030400f03SJulian Elischer (*node->nd_type->shutdown)(node); 73130400f03SJulian Elischer if (NG_NODE_IS_VALID(node)) { 73230400f03SJulian Elischer /* 73330400f03SJulian Elischer * Well, blow me down if the node code hasn't declared 73430400f03SJulian Elischer * that it doesn't want to die. 73530400f03SJulian Elischer * Presumably it is a persistant node. 7361acb27c6SJulian Elischer * If we REALLY want it to go away, 7371acb27c6SJulian Elischer * e.g. hardware going away, 738be4252b3SJulian Elischer * Our caller should set NGF_REALLY_DIE in nd_flags. 73930400f03SJulian Elischer */ 740be4252b3SJulian Elischer node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING); 7411acb27c6SJulian Elischer NG_NODE_UNREF(node); /* Assume they still have theirs */ 74230400f03SJulian Elischer return; 7434cf49a43SJulian Elischer } 7441acb27c6SJulian Elischer } else { /* do the default thing */ 7451acb27c6SJulian Elischer NG_NODE_UNREF(node); 7461acb27c6SJulian Elischer } 7474cf49a43SJulian Elischer 74830400f03SJulian Elischer ng_unname(node); /* basically a NOP these days */ 74930400f03SJulian Elischer 75030400f03SJulian Elischer /* 75130400f03SJulian Elischer * Remove extra reference, possibly the last 75230400f03SJulian Elischer * Possible other holders of references may include 75330400f03SJulian Elischer * timeout callouts, but theoretically the node's supposed to 75430400f03SJulian Elischer * have cancelled them. Possibly hardware dependencies may 75530400f03SJulian Elischer * force a driver to 'linger' with a reference. 75630400f03SJulian Elischer */ 75730400f03SJulian Elischer NG_NODE_UNREF(node); 7584cf49a43SJulian Elischer } 7594cf49a43SJulian Elischer 7605951069aSJulian Elischer /* 7615951069aSJulian Elischer * Remove a reference to the node, possibly the last. 7625951069aSJulian Elischer * deadnode always acts as it it were the last. 7635951069aSJulian Elischer */ 7645951069aSJulian Elischer int 76530400f03SJulian Elischer ng_unref_node(node_p node) 7664cf49a43SJulian Elischer { 76730400f03SJulian Elischer int v; 7686b795970SJulian Elischer 7696b795970SJulian Elischer if (node == &ng_deadnode) { 7705951069aSJulian Elischer return (0); 7716b795970SJulian Elischer } 7726b795970SJulian Elischer 773018fe3d1SAlexander Motin v = atomic_fetchadd_int(&node->nd_refs, -1); 774e8a49db2SJulian Elischer 775018fe3d1SAlexander Motin if (v == 1) { /* we were the last */ 776069154d5SJulian Elischer 777cfea3f85SAlexander Motin mtx_lock(&ng_namehash_mtx); 77830400f03SJulian Elischer node->nd_type->refs--; /* XXX maybe should get types lock? */ 77930400f03SJulian Elischer LIST_REMOVE(node, nd_nodes); 780cfea3f85SAlexander Motin mtx_unlock(&ng_namehash_mtx); 781069154d5SJulian Elischer 7829ed346baSBosko Milekic mtx_lock(&ng_idhash_mtx); 78330400f03SJulian Elischer LIST_REMOVE(node, nd_idnodes); 7849ed346baSBosko Milekic mtx_unlock(&ng_idhash_mtx); 785069154d5SJulian Elischer 78612574a02SJulian Elischer mtx_destroy(&node->nd_input_queue.q_mtx); 787069154d5SJulian Elischer NG_FREE_NODE(node); 7884cf49a43SJulian Elischer } 789018fe3d1SAlexander Motin return (v - 1); 7904cf49a43SJulian Elischer } 7914cf49a43SJulian Elischer 7924cf49a43SJulian Elischer /************************************************************************ 793dc90cad9SJulian Elischer Node ID handling 794dc90cad9SJulian Elischer ************************************************************************/ 795dc90cad9SJulian Elischer static node_p 796069154d5SJulian Elischer ng_ID2noderef(ng_ID_t ID) 797dc90cad9SJulian Elischer { 79830400f03SJulian Elischer node_p node; 7999ed346baSBosko Milekic mtx_lock(&ng_idhash_mtx); 8000f150d04SJulian Elischer NG_IDHASH_FIND(ID, node); 80130400f03SJulian Elischer if(node) 80230400f03SJulian Elischer NG_NODE_REF(node); 8039ed346baSBosko Milekic mtx_unlock(&ng_idhash_mtx); 80430400f03SJulian Elischer return(node); 805dc90cad9SJulian Elischer } 806dc90cad9SJulian Elischer 807dc90cad9SJulian Elischer ng_ID_t 808dc90cad9SJulian Elischer ng_node2ID(node_p node) 809dc90cad9SJulian Elischer { 81070de87f2SJulian Elischer return (node ? NG_NODE_ID(node) : 0); 811dc90cad9SJulian Elischer } 812dc90cad9SJulian Elischer 813dc90cad9SJulian Elischer /************************************************************************ 8144cf49a43SJulian Elischer Node name handling 8154cf49a43SJulian Elischer ************************************************************************/ 8164cf49a43SJulian Elischer 8174cf49a43SJulian Elischer /* 8184cf49a43SJulian Elischer * Assign a node a name. Once assigned, the name cannot be changed. 8194cf49a43SJulian Elischer */ 8204cf49a43SJulian Elischer int 8214cf49a43SJulian Elischer ng_name_node(node_p node, const char *name) 8224cf49a43SJulian Elischer { 823cfea3f85SAlexander Motin int i, hash; 824069154d5SJulian Elischer node_p node2; 8254cf49a43SJulian Elischer 8264cf49a43SJulian Elischer /* Check the name is valid */ 82787e2c66aSHartmut Brandt for (i = 0; i < NG_NODESIZ; i++) { 8284cf49a43SJulian Elischer if (name[i] == '\0' || name[i] == '.' || name[i] == ':') 8294cf49a43SJulian Elischer break; 8304cf49a43SJulian Elischer } 8314cf49a43SJulian Elischer if (i == 0 || name[i] != '\0') { 8326b795970SJulian Elischer TRAP_ERROR(); 8334cf49a43SJulian Elischer return (EINVAL); 8344cf49a43SJulian Elischer } 835dc90cad9SJulian Elischer if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ 8366b795970SJulian Elischer TRAP_ERROR(); 8374cf49a43SJulian Elischer return (EINVAL); 8384cf49a43SJulian Elischer } 8394cf49a43SJulian Elischer 8404cf49a43SJulian Elischer /* Check the name isn't already being used */ 841069154d5SJulian Elischer if ((node2 = ng_name2noderef(node, name)) != NULL) { 84230400f03SJulian Elischer NG_NODE_UNREF(node2); 8436b795970SJulian Elischer TRAP_ERROR(); 8444cf49a43SJulian Elischer return (EADDRINUSE); 8454cf49a43SJulian Elischer } 8464cf49a43SJulian Elischer 847069154d5SJulian Elischer /* copy it */ 84887e2c66aSHartmut Brandt strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ); 8494cf49a43SJulian Elischer 850cfea3f85SAlexander Motin /* Update name hash. */ 851cfea3f85SAlexander Motin NG_NAMEHASH(name, hash); 852cfea3f85SAlexander Motin mtx_lock(&ng_namehash_mtx); 853cfea3f85SAlexander Motin LIST_REMOVE(node, nd_nodes); 854cfea3f85SAlexander Motin LIST_INSERT_HEAD(&ng_name_hash[hash], node, nd_nodes); 855cfea3f85SAlexander Motin mtx_unlock(&ng_namehash_mtx); 856cfea3f85SAlexander Motin 8574cf49a43SJulian Elischer return (0); 8584cf49a43SJulian Elischer } 8594cf49a43SJulian Elischer 8604cf49a43SJulian Elischer /* 8614cf49a43SJulian Elischer * Find a node by absolute name. The name should NOT end with ':' 8624cf49a43SJulian Elischer * The name "." means "this node" and "[xxx]" means "the node 8634cf49a43SJulian Elischer * with ID (ie, at address) xxx". 8644cf49a43SJulian Elischer * 8654cf49a43SJulian Elischer * Returns the node if found, else NULL. 866069154d5SJulian Elischer * Eventually should add something faster than a sequential search. 867e1e8f51bSRobert Watson * Note it acquires a reference on the node so you can be sure it's still 868e1e8f51bSRobert Watson * there. 8694cf49a43SJulian Elischer */ 8704cf49a43SJulian Elischer node_p 871069154d5SJulian Elischer ng_name2noderef(node_p here, const char *name) 8724cf49a43SJulian Elischer { 873dc90cad9SJulian Elischer node_p node; 874dc90cad9SJulian Elischer ng_ID_t temp; 875cfea3f85SAlexander Motin int hash; 8764cf49a43SJulian Elischer 8774cf49a43SJulian Elischer /* "." means "this node" */ 878069154d5SJulian Elischer if (strcmp(name, ".") == 0) { 87930400f03SJulian Elischer NG_NODE_REF(here); 880069154d5SJulian Elischer return(here); 881069154d5SJulian Elischer } 8824cf49a43SJulian Elischer 8834cf49a43SJulian Elischer /* Check for name-by-ID */ 884dc90cad9SJulian Elischer if ((temp = ng_decodeidname(name)) != 0) { 885069154d5SJulian Elischer return (ng_ID2noderef(temp)); 8864cf49a43SJulian Elischer } 8874cf49a43SJulian Elischer 8884cf49a43SJulian Elischer /* Find node by name */ 889cfea3f85SAlexander Motin NG_NAMEHASH(name, hash); 890cfea3f85SAlexander Motin mtx_lock(&ng_namehash_mtx); 891cfea3f85SAlexander Motin LIST_FOREACH(node, &ng_name_hash[hash], nd_nodes) { 892cfea3f85SAlexander Motin if (NG_NODE_IS_VALID(node) && 893cfea3f85SAlexander Motin (strcmp(NG_NODE_NAME(node), name) == 0)) { 8944cf49a43SJulian Elischer break; 8954cf49a43SJulian Elischer } 89670de87f2SJulian Elischer } 897069154d5SJulian Elischer if (node) 89830400f03SJulian Elischer NG_NODE_REF(node); 899cfea3f85SAlexander Motin mtx_unlock(&ng_namehash_mtx); 9004cf49a43SJulian Elischer return (node); 9014cf49a43SJulian Elischer } 9024cf49a43SJulian Elischer 9034cf49a43SJulian Elischer /* 9049d5abbddSJens Schweikhardt * Decode an ID name, eg. "[f03034de]". Returns 0 if the 905dc90cad9SJulian Elischer * string is not valid, otherwise returns the value. 9064cf49a43SJulian Elischer */ 907dc90cad9SJulian Elischer static ng_ID_t 9084cf49a43SJulian Elischer ng_decodeidname(const char *name) 9094cf49a43SJulian Elischer { 9102b70adcbSArchie Cobbs const int len = strlen(name); 91125792ef3SArchie Cobbs char *eptr; 9122b70adcbSArchie Cobbs u_long val; 9134cf49a43SJulian Elischer 9142b70adcbSArchie Cobbs /* Check for proper length, brackets, no leading junk */ 91570de87f2SJulian Elischer if ((len < 3) 91670de87f2SJulian Elischer || (name[0] != '[') 91770de87f2SJulian Elischer || (name[len - 1] != ']') 91870de87f2SJulian Elischer || (!isxdigit(name[1]))) { 91970de87f2SJulian Elischer return ((ng_ID_t)0); 92070de87f2SJulian Elischer } 9214cf49a43SJulian Elischer 9222b70adcbSArchie Cobbs /* Decode number */ 9232b70adcbSArchie Cobbs val = strtoul(name + 1, &eptr, 16); 92470de87f2SJulian Elischer if ((eptr - name != len - 1) 92570de87f2SJulian Elischer || (val == ULONG_MAX) 92670de87f2SJulian Elischer || (val == 0)) { 92712f035e0SJulian Elischer return ((ng_ID_t)0); 92870de87f2SJulian Elischer } 9292b70adcbSArchie Cobbs return (ng_ID_t)val; 9304cf49a43SJulian Elischer } 9314cf49a43SJulian Elischer 9324cf49a43SJulian Elischer /* 9334cf49a43SJulian Elischer * Remove a name from a node. This should only be called 9344cf49a43SJulian Elischer * when shutting down and removing the node. 93564efc707SRobert Watson * IF we allow name changing this may be more resurrected. 9364cf49a43SJulian Elischer */ 9374cf49a43SJulian Elischer void 9384cf49a43SJulian Elischer ng_unname(node_p node) 9394cf49a43SJulian Elischer { 9404cf49a43SJulian Elischer } 9414cf49a43SJulian Elischer 9424cf49a43SJulian Elischer /************************************************************************ 9434cf49a43SJulian Elischer Hook routines 9444cf49a43SJulian Elischer Names are not optional. Hooks are always connected, except for a 9453e4084c8SJulian Elischer brief moment within these routines. On invalidation or during creation 9463e4084c8SJulian Elischer they are connected to the 'dead' hook. 9474cf49a43SJulian Elischer ************************************************************************/ 9484cf49a43SJulian Elischer 9494cf49a43SJulian Elischer /* 9504cf49a43SJulian Elischer * Remove a hook reference 9514cf49a43SJulian Elischer */ 95230400f03SJulian Elischer void 9534cf49a43SJulian Elischer ng_unref_hook(hook_p hook) 9544cf49a43SJulian Elischer { 95530400f03SJulian Elischer int v; 9566b795970SJulian Elischer 9576b795970SJulian Elischer if (hook == &ng_deadhook) { 9586b795970SJulian Elischer return; 9596b795970SJulian Elischer } 960018fe3d1SAlexander Motin 961018fe3d1SAlexander Motin v = atomic_fetchadd_int(&hook->hk_refs, -1); 962e8a49db2SJulian Elischer 96330400f03SJulian Elischer if (v == 1) { /* we were the last */ 964f573da1aSAlexander Motin if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */ 9656b795970SJulian Elischer _NG_NODE_UNREF((_NG_HOOK_NODE(hook))); 966069154d5SJulian Elischer NG_FREE_HOOK(hook); 967069154d5SJulian Elischer } 9684cf49a43SJulian Elischer } 9694cf49a43SJulian Elischer 9704cf49a43SJulian Elischer /* 9714cf49a43SJulian Elischer * Add an unconnected hook to a node. Only used internally. 9723e4084c8SJulian Elischer * Assumes node is locked. (XXX not yet true ) 9734cf49a43SJulian Elischer */ 9744cf49a43SJulian Elischer static int 9754cf49a43SJulian Elischer ng_add_hook(node_p node, const char *name, hook_p *hookp) 9764cf49a43SJulian Elischer { 9774cf49a43SJulian Elischer hook_p hook; 9784cf49a43SJulian Elischer int error = 0; 9794cf49a43SJulian Elischer 9804cf49a43SJulian Elischer /* Check that the given name is good */ 9814cf49a43SJulian Elischer if (name == NULL) { 9826b795970SJulian Elischer TRAP_ERROR(); 9834cf49a43SJulian Elischer return (EINVAL); 9844cf49a43SJulian Elischer } 985899e9c4eSArchie Cobbs if (ng_findhook(node, name) != NULL) { 9866b795970SJulian Elischer TRAP_ERROR(); 9874cf49a43SJulian Elischer return (EEXIST); 9884cf49a43SJulian Elischer } 9894cf49a43SJulian Elischer 9904cf49a43SJulian Elischer /* Allocate the hook and link it up */ 99130400f03SJulian Elischer NG_ALLOC_HOOK(hook); 9924cf49a43SJulian Elischer if (hook == NULL) { 9936b795970SJulian Elischer TRAP_ERROR(); 9944cf49a43SJulian Elischer return (ENOMEM); 9954cf49a43SJulian Elischer } 9963e4084c8SJulian Elischer hook->hk_refs = 1; /* add a reference for us to return */ 99730400f03SJulian Elischer hook->hk_flags = HK_INVALID; 9983e4084c8SJulian Elischer hook->hk_peer = &ng_deadhook; /* start off this way */ 99930400f03SJulian Elischer hook->hk_node = node; 100030400f03SJulian Elischer NG_NODE_REF(node); /* each hook counts as a reference */ 10014cf49a43SJulian Elischer 10023e4084c8SJulian Elischer /* Set hook name */ 100387e2c66aSHartmut Brandt strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ); 10043e4084c8SJulian Elischer 10053e4084c8SJulian Elischer /* 10063e4084c8SJulian Elischer * Check if the node type code has something to say about it 10073e4084c8SJulian Elischer * If it fails, the unref of the hook will also unref the node. 10083e4084c8SJulian Elischer */ 1009954c4772SJulian Elischer if (node->nd_type->newhook != NULL) { 1010954c4772SJulian Elischer if ((error = (*node->nd_type->newhook)(node, hook, name))) { 101130400f03SJulian Elischer NG_HOOK_UNREF(hook); /* this frees the hook */ 1012069154d5SJulian Elischer return (error); 1013069154d5SJulian Elischer } 1014954c4772SJulian Elischer } 10154cf49a43SJulian Elischer /* 10164cf49a43SJulian Elischer * The 'type' agrees so far, so go ahead and link it in. 10174cf49a43SJulian Elischer * We'll ask again later when we actually connect the hooks. 10184cf49a43SJulian Elischer */ 101930400f03SJulian Elischer LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 102030400f03SJulian Elischer node->nd_numhooks++; 10213e4084c8SJulian Elischer NG_HOOK_REF(hook); /* one for the node */ 10224cf49a43SJulian Elischer 10234cf49a43SJulian Elischer if (hookp) 10244cf49a43SJulian Elischer *hookp = hook; 10253e4084c8SJulian Elischer return (0); 10264cf49a43SJulian Elischer } 10274cf49a43SJulian Elischer 10284cf49a43SJulian Elischer /* 1029899e9c4eSArchie Cobbs * Find a hook 1030899e9c4eSArchie Cobbs * 1031899e9c4eSArchie Cobbs * Node types may supply their own optimized routines for finding 1032899e9c4eSArchie Cobbs * hooks. If none is supplied, we just do a linear search. 10333e4084c8SJulian Elischer * XXX Possibly we should add a reference to the hook? 1034899e9c4eSArchie Cobbs */ 1035899e9c4eSArchie Cobbs hook_p 1036899e9c4eSArchie Cobbs ng_findhook(node_p node, const char *name) 1037899e9c4eSArchie Cobbs { 1038899e9c4eSArchie Cobbs hook_p hook; 1039899e9c4eSArchie Cobbs 104030400f03SJulian Elischer if (node->nd_type->findhook != NULL) 104130400f03SJulian Elischer return (*node->nd_type->findhook)(node, name); 104230400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 104370de87f2SJulian Elischer if (NG_HOOK_IS_VALID(hook) 1044ce5e5f99SArchie Cobbs && (strcmp(NG_HOOK_NAME(hook), name) == 0)) 1045899e9c4eSArchie Cobbs return (hook); 1046899e9c4eSArchie Cobbs } 1047899e9c4eSArchie Cobbs return (NULL); 1048899e9c4eSArchie Cobbs } 1049899e9c4eSArchie Cobbs 1050899e9c4eSArchie Cobbs /* 10514cf49a43SJulian Elischer * Destroy a hook 10524cf49a43SJulian Elischer * 10534cf49a43SJulian Elischer * As hooks are always attached, this really destroys two hooks. 10544cf49a43SJulian Elischer * The one given, and the one attached to it. Disconnect the hooks 10553e4084c8SJulian Elischer * from each other first. We reconnect the peer hook to the 'dead' 10563e4084c8SJulian Elischer * hook so that it can still exist after we depart. We then 10573e4084c8SJulian Elischer * send the peer its own destroy message. This ensures that we only 10583e4084c8SJulian Elischer * interact with the peer's structures when it is locked processing that 10593e4084c8SJulian Elischer * message. We hold a reference to the peer hook so we are guaranteed that 10603e4084c8SJulian Elischer * the peer hook and node are still going to exist until 10613e4084c8SJulian Elischer * we are finished there as the hook holds a ref on the node. 10623e4084c8SJulian Elischer * We run this same code again on the peer hook, but that time it is already 10633e4084c8SJulian Elischer * attached to the 'dead' hook. 10646b795970SJulian Elischer * 10656b795970SJulian Elischer * This routine is called at all stages of hook creation 10666b795970SJulian Elischer * on error detection and must be able to handle any such stage. 10674cf49a43SJulian Elischer */ 10684cf49a43SJulian Elischer void 10694cf49a43SJulian Elischer ng_destroy_hook(hook_p hook) 10704cf49a43SJulian Elischer { 1071ac5dd141SGleb Smirnoff hook_p peer; 1072ac5dd141SGleb Smirnoff node_p node; 10734cf49a43SJulian Elischer 10746b795970SJulian Elischer if (hook == &ng_deadhook) { /* better safe than sorry */ 10756b795970SJulian Elischer printf("ng_destroy_hook called on deadhook\n"); 10766b795970SJulian Elischer return; 10776b795970SJulian Elischer } 1078ac5dd141SGleb Smirnoff 1079ac5dd141SGleb Smirnoff /* 1080ac5dd141SGleb Smirnoff * Protect divorce process with mutex, to avoid races on 1081ac5dd141SGleb Smirnoff * simultaneous disconnect. 1082ac5dd141SGleb Smirnoff */ 1083ac5dd141SGleb Smirnoff mtx_lock(&ng_topo_mtx); 1084ac5dd141SGleb Smirnoff 1085ac5dd141SGleb Smirnoff hook->hk_flags |= HK_INVALID; 1086ac5dd141SGleb Smirnoff 1087ac5dd141SGleb Smirnoff peer = NG_HOOK_PEER(hook); 1088ac5dd141SGleb Smirnoff node = NG_HOOK_NODE(hook); 1089ac5dd141SGleb Smirnoff 10903e4084c8SJulian Elischer if (peer && (peer != &ng_deadhook)) { 10913e4084c8SJulian Elischer /* 10923e4084c8SJulian Elischer * Set the peer to point to ng_deadhook 10933e4084c8SJulian Elischer * from this moment on we are effectively independent it. 10943e4084c8SJulian Elischer * send it an rmhook message of it's own. 10953e4084c8SJulian Elischer */ 10963e4084c8SJulian Elischer peer->hk_peer = &ng_deadhook; /* They no longer know us */ 10973e4084c8SJulian Elischer hook->hk_peer = &ng_deadhook; /* Nor us, them */ 10986b795970SJulian Elischer if (NG_HOOK_NODE(peer) == &ng_deadnode) { 10996b795970SJulian Elischer /* 11006b795970SJulian Elischer * If it's already divorced from a node, 11016b795970SJulian Elischer * just free it. 11026b795970SJulian Elischer */ 1103ac5dd141SGleb Smirnoff mtx_unlock(&ng_topo_mtx); 11046b795970SJulian Elischer } else { 1105ac5dd141SGleb Smirnoff mtx_unlock(&ng_topo_mtx); 11066b795970SJulian Elischer ng_rmhook_self(peer); /* Send it a surprise */ 11076b795970SJulian Elischer } 110852fa3556SJulian Elischer NG_HOOK_UNREF(peer); /* account for peer link */ 110952fa3556SJulian Elischer NG_HOOK_UNREF(hook); /* account for peer link */ 1110ac5dd141SGleb Smirnoff } else 1111ac5dd141SGleb Smirnoff mtx_unlock(&ng_topo_mtx); 1112ac5dd141SGleb Smirnoff 1113ac5dd141SGleb Smirnoff mtx_assert(&ng_topo_mtx, MA_NOTOWNED); 11144cf49a43SJulian Elischer 11154cf49a43SJulian Elischer /* 11164cf49a43SJulian Elischer * Remove the hook from the node's list to avoid possible recursion 11174cf49a43SJulian Elischer * in case the disconnection results in node shutdown. 11184cf49a43SJulian Elischer */ 11196b795970SJulian Elischer if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */ 11206b795970SJulian Elischer return; 11216b795970SJulian Elischer } 112230400f03SJulian Elischer LIST_REMOVE(hook, hk_hooks); 112330400f03SJulian Elischer node->nd_numhooks--; 112430400f03SJulian Elischer if (node->nd_type->disconnect) { 11254cf49a43SJulian Elischer /* 11266b795970SJulian Elischer * The type handler may elect to destroy the node so don't 112764efc707SRobert Watson * trust its existence after this point. (except 11286b795970SJulian Elischer * that we still hold a reference on it. (which we 11296b795970SJulian Elischer * inherrited from the hook we are destroying) 11304cf49a43SJulian Elischer */ 113130400f03SJulian Elischer (*node->nd_type->disconnect) (hook); 11324cf49a43SJulian Elischer } 11336b795970SJulian Elischer 11346b795970SJulian Elischer /* 11356b795970SJulian Elischer * Note that because we will point to ng_deadnode, the original node 11366b795970SJulian Elischer * is not decremented automatically so we do that manually. 11376b795970SJulian Elischer */ 11386b795970SJulian Elischer _NG_HOOK_NODE(hook) = &ng_deadnode; 11396b795970SJulian Elischer NG_NODE_UNREF(node); /* We no longer point to it so adjust count */ 11406b795970SJulian Elischer NG_HOOK_UNREF(hook); /* Account for linkage (in list) to node */ 11414cf49a43SJulian Elischer } 11424cf49a43SJulian Elischer 11434cf49a43SJulian Elischer /* 11444cf49a43SJulian Elischer * Take two hooks on a node and merge the connection so that the given node 11454cf49a43SJulian Elischer * is effectively bypassed. 11464cf49a43SJulian Elischer */ 11474cf49a43SJulian Elischer int 11484cf49a43SJulian Elischer ng_bypass(hook_p hook1, hook_p hook2) 11494cf49a43SJulian Elischer { 115030400f03SJulian Elischer if (hook1->hk_node != hook2->hk_node) { 11516b795970SJulian Elischer TRAP_ERROR(); 11524cf49a43SJulian Elischer return (EINVAL); 115330400f03SJulian Elischer } 115430400f03SJulian Elischer hook1->hk_peer->hk_peer = hook2->hk_peer; 115530400f03SJulian Elischer hook2->hk_peer->hk_peer = hook1->hk_peer; 11564cf49a43SJulian Elischer 11573e4084c8SJulian Elischer hook1->hk_peer = &ng_deadhook; 11583e4084c8SJulian Elischer hook2->hk_peer = &ng_deadhook; 11593e4084c8SJulian Elischer 1160cf3254aaSGleb Smirnoff NG_HOOK_UNREF(hook1); 1161cf3254aaSGleb Smirnoff NG_HOOK_UNREF(hook2); 1162cf3254aaSGleb Smirnoff 11634cf49a43SJulian Elischer /* XXX If we ever cache methods on hooks update them as well */ 11644cf49a43SJulian Elischer ng_destroy_hook(hook1); 11654cf49a43SJulian Elischer ng_destroy_hook(hook2); 11664cf49a43SJulian Elischer return (0); 11674cf49a43SJulian Elischer } 11684cf49a43SJulian Elischer 11694cf49a43SJulian Elischer /* 11704cf49a43SJulian Elischer * Install a new netgraph type 11714cf49a43SJulian Elischer */ 11724cf49a43SJulian Elischer int 11734cf49a43SJulian Elischer ng_newtype(struct ng_type *tp) 11744cf49a43SJulian Elischer { 11754cf49a43SJulian Elischer const size_t namelen = strlen(tp->name); 11764cf49a43SJulian Elischer 11774cf49a43SJulian Elischer /* Check version and type name fields */ 1178589f6ed8SJulian Elischer if ((tp->version != NG_ABI_VERSION) 1179589f6ed8SJulian Elischer || (namelen == 0) 118087e2c66aSHartmut Brandt || (namelen >= NG_TYPESIZ)) { 11816b795970SJulian Elischer TRAP_ERROR(); 11828ed370fdSJulian Elischer if (tp->version != NG_ABI_VERSION) { 11838ed370fdSJulian Elischer printf("Netgraph: Node type rejected. ABI mismatch. Suggest recompile\n"); 11848ed370fdSJulian Elischer } 11854cf49a43SJulian Elischer return (EINVAL); 11864cf49a43SJulian Elischer } 11874cf49a43SJulian Elischer 11884cf49a43SJulian Elischer /* Check for name collision */ 11894cf49a43SJulian Elischer if (ng_findtype(tp->name) != NULL) { 11906b795970SJulian Elischer TRAP_ERROR(); 11914cf49a43SJulian Elischer return (EEXIST); 11924cf49a43SJulian Elischer } 11934cf49a43SJulian Elischer 1194069154d5SJulian Elischer 1195069154d5SJulian Elischer /* Link in new type */ 11969ed346baSBosko Milekic mtx_lock(&ng_typelist_mtx); 1197069154d5SJulian Elischer LIST_INSERT_HEAD(&ng_typelist, tp, types); 1198c73b94a2SJulian Elischer tp->refs = 1; /* first ref is linked list */ 11999ed346baSBosko Milekic mtx_unlock(&ng_typelist_mtx); 12004cf49a43SJulian Elischer return (0); 12014cf49a43SJulian Elischer } 12024cf49a43SJulian Elischer 12034cf49a43SJulian Elischer /* 1204c31b4a53SJulian Elischer * unlink a netgraph type 1205c31b4a53SJulian Elischer * If no examples exist 1206c31b4a53SJulian Elischer */ 1207c31b4a53SJulian Elischer int 1208c31b4a53SJulian Elischer ng_rmtype(struct ng_type *tp) 1209c31b4a53SJulian Elischer { 1210c31b4a53SJulian Elischer /* Check for name collision */ 1211c31b4a53SJulian Elischer if (tp->refs != 1) { 1212c31b4a53SJulian Elischer TRAP_ERROR(); 1213c31b4a53SJulian Elischer return (EBUSY); 1214c31b4a53SJulian Elischer } 1215c31b4a53SJulian Elischer 1216c31b4a53SJulian Elischer /* Unlink type */ 1217c31b4a53SJulian Elischer mtx_lock(&ng_typelist_mtx); 1218c31b4a53SJulian Elischer LIST_REMOVE(tp, types); 1219c31b4a53SJulian Elischer mtx_unlock(&ng_typelist_mtx); 1220c31b4a53SJulian Elischer return (0); 1221c31b4a53SJulian Elischer } 1222c31b4a53SJulian Elischer 1223c31b4a53SJulian Elischer /* 12244cf49a43SJulian Elischer * Look for a type of the name given 12254cf49a43SJulian Elischer */ 12264cf49a43SJulian Elischer struct ng_type * 12274cf49a43SJulian Elischer ng_findtype(const char *typename) 12284cf49a43SJulian Elischer { 12294cf49a43SJulian Elischer struct ng_type *type; 12304cf49a43SJulian Elischer 12319ed346baSBosko Milekic mtx_lock(&ng_typelist_mtx); 1232069154d5SJulian Elischer LIST_FOREACH(type, &ng_typelist, types) { 12334cf49a43SJulian Elischer if (strcmp(type->name, typename) == 0) 12344cf49a43SJulian Elischer break; 12354cf49a43SJulian Elischer } 12369ed346baSBosko Milekic mtx_unlock(&ng_typelist_mtx); 12374cf49a43SJulian Elischer return (type); 12384cf49a43SJulian Elischer } 12394cf49a43SJulian Elischer 12404cf49a43SJulian Elischer /************************************************************************ 12414cf49a43SJulian Elischer Composite routines 12424cf49a43SJulian Elischer ************************************************************************/ 12434cf49a43SJulian Elischer /* 12446b795970SJulian Elischer * Connect two nodes using the specified hooks, using queued functions. 12454cf49a43SJulian Elischer */ 1246e088dd4cSAlexander Motin static int 1247e088dd4cSAlexander Motin ng_con_part3(node_p node, item_p item, hook_p hook) 12484cf49a43SJulian Elischer { 1249e088dd4cSAlexander Motin int error = 0; 12504cf49a43SJulian Elischer 12516b795970SJulian Elischer /* 12526b795970SJulian Elischer * When we run, we know that the node 'node' is locked for us. 12536b795970SJulian Elischer * Our caller has a reference on the hook. 12546b795970SJulian Elischer * Our caller has a reference on the node. 12556b795970SJulian Elischer * (In this case our caller is ng_apply_item() ). 12566b795970SJulian Elischer * The peer hook has a reference on the hook. 12571acb27c6SJulian Elischer * We are all set up except for the final call to the node, and 12581acb27c6SJulian Elischer * the clearing of the INVALID flag. 12596b795970SJulian Elischer */ 12606b795970SJulian Elischer if (NG_HOOK_NODE(hook) == &ng_deadnode) { 12616b795970SJulian Elischer /* 12626b795970SJulian Elischer * The node must have been freed again since we last visited 12636b795970SJulian Elischer * here. ng_destry_hook() has this effect but nothing else does. 12646b795970SJulian Elischer * We should just release our references and 12656b795970SJulian Elischer * free anything we can think of. 12666b795970SJulian Elischer * Since we know it's been destroyed, and it's our caller 12676b795970SJulian Elischer * that holds the references, just return. 12686b795970SJulian Elischer */ 1269e088dd4cSAlexander Motin ERROUT(ENOENT); 12706b795970SJulian Elischer } 12716b795970SJulian Elischer if (hook->hk_node->nd_type->connect) { 1272e088dd4cSAlexander Motin if ((error = (*hook->hk_node->nd_type->connect) (hook))) { 12736b795970SJulian Elischer ng_destroy_hook(hook); /* also zaps peer */ 12741acb27c6SJulian Elischer printf("failed in ng_con_part3()\n"); 1275e088dd4cSAlexander Motin ERROUT(error); 12764cf49a43SJulian Elischer } 12776b795970SJulian Elischer } 12786b795970SJulian Elischer /* 12796b795970SJulian Elischer * XXX this is wrong for SMP. Possibly we need 12806b795970SJulian Elischer * to separate out 'create' and 'invalid' flags. 12816b795970SJulian Elischer * should only set flags on hooks we have locked under our node. 12826b795970SJulian Elischer */ 12836b795970SJulian Elischer hook->hk_flags &= ~HK_INVALID; 1284e088dd4cSAlexander Motin done: 1285e088dd4cSAlexander Motin NG_FREE_ITEM(item); 1286e088dd4cSAlexander Motin return (error); 12876b795970SJulian Elischer } 12886b795970SJulian Elischer 1289e088dd4cSAlexander Motin static int 1290e088dd4cSAlexander Motin ng_con_part2(node_p node, item_p item, hook_p hook) 12916b795970SJulian Elischer { 1292ac5dd141SGleb Smirnoff hook_p peer; 1293e088dd4cSAlexander Motin int error = 0; 12946b795970SJulian Elischer 12956b795970SJulian Elischer /* 12966b795970SJulian Elischer * When we run, we know that the node 'node' is locked for us. 12976b795970SJulian Elischer * Our caller has a reference on the hook. 12986b795970SJulian Elischer * Our caller has a reference on the node. 12996b795970SJulian Elischer * (In this case our caller is ng_apply_item() ). 13006b795970SJulian Elischer * The peer hook has a reference on the hook. 13016b795970SJulian Elischer * our node pointer points to the 'dead' node. 13026b795970SJulian Elischer * First check the hook name is unique. 13031acb27c6SJulian Elischer * Should not happen because we checked before queueing this. 13046b795970SJulian Elischer */ 13056b795970SJulian Elischer if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) { 13066b795970SJulian Elischer TRAP_ERROR(); 13076b795970SJulian Elischer ng_destroy_hook(hook); /* should destroy peer too */ 13081acb27c6SJulian Elischer printf("failed in ng_con_part2()\n"); 1309e088dd4cSAlexander Motin ERROUT(EEXIST); 13106b795970SJulian Elischer } 13116b795970SJulian Elischer /* 13126b795970SJulian Elischer * Check if the node type code has something to say about it 13136b795970SJulian Elischer * If it fails, the unref of the hook will also unref the attached node, 13146b795970SJulian Elischer * however since that node is 'ng_deadnode' this will do nothing. 13156b795970SJulian Elischer * The peer hook will also be destroyed. 13166b795970SJulian Elischer */ 13176b795970SJulian Elischer if (node->nd_type->newhook != NULL) { 1318e088dd4cSAlexander Motin if ((error = (*node->nd_type->newhook)(node, hook, 1319e088dd4cSAlexander Motin hook->hk_name))) { 13206b795970SJulian Elischer ng_destroy_hook(hook); /* should destroy peer too */ 13211acb27c6SJulian Elischer printf("failed in ng_con_part2()\n"); 1322e088dd4cSAlexander Motin ERROUT(error); 13236b795970SJulian Elischer } 13246b795970SJulian Elischer } 13256b795970SJulian Elischer 13266b795970SJulian Elischer /* 13276b795970SJulian Elischer * The 'type' agrees so far, so go ahead and link it in. 13286b795970SJulian Elischer * We'll ask again later when we actually connect the hooks. 13296b795970SJulian Elischer */ 13306b795970SJulian Elischer hook->hk_node = node; /* just overwrite ng_deadnode */ 13316b795970SJulian Elischer NG_NODE_REF(node); /* each hook counts as a reference */ 13326b795970SJulian Elischer LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 13336b795970SJulian Elischer node->nd_numhooks++; 13346b795970SJulian Elischer NG_HOOK_REF(hook); /* one for the node */ 13356b795970SJulian Elischer 13366b795970SJulian Elischer /* 133764efc707SRobert Watson * We now have a symmetrical situation, where both hooks have been 13385951069aSJulian Elischer * linked to their nodes, the newhook methods have been called 13396b795970SJulian Elischer * And the references are all correct. The hooks are still marked 13406b795970SJulian Elischer * as invalid, as we have not called the 'connect' methods 13416b795970SJulian Elischer * yet. 134264efc707SRobert Watson * We can call the local one immediately as we have the 13436b795970SJulian Elischer * node locked, but we need to queue the remote one. 13446b795970SJulian Elischer */ 13456b795970SJulian Elischer if (hook->hk_node->nd_type->connect) { 1346e088dd4cSAlexander Motin if ((error = (*hook->hk_node->nd_type->connect) (hook))) { 13476b795970SJulian Elischer ng_destroy_hook(hook); /* also zaps peer */ 13481acb27c6SJulian Elischer printf("failed in ng_con_part2(A)\n"); 1349e088dd4cSAlexander Motin ERROUT(error); 13506b795970SJulian Elischer } 13516b795970SJulian Elischer } 1352ac5dd141SGleb Smirnoff 1353ac5dd141SGleb Smirnoff /* 1354ac5dd141SGleb Smirnoff * Acquire topo mutex to avoid race with ng_destroy_hook(). 1355ac5dd141SGleb Smirnoff */ 1356ac5dd141SGleb Smirnoff mtx_lock(&ng_topo_mtx); 1357ac5dd141SGleb Smirnoff peer = hook->hk_peer; 1358ac5dd141SGleb Smirnoff if (peer == &ng_deadhook) { 1359ac5dd141SGleb Smirnoff mtx_unlock(&ng_topo_mtx); 1360ac5dd141SGleb Smirnoff printf("failed in ng_con_part2(B)\n"); 1361ac5dd141SGleb Smirnoff ng_destroy_hook(hook); 1362e088dd4cSAlexander Motin ERROUT(ENOENT); 1363ac5dd141SGleb Smirnoff } 1364ac5dd141SGleb Smirnoff mtx_unlock(&ng_topo_mtx); 1365ac5dd141SGleb Smirnoff 1366b332b91fSGleb Smirnoff if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3, 1367b332b91fSGleb Smirnoff NULL, 0, NG_REUSE_ITEM))) { 1368ac5dd141SGleb Smirnoff printf("failed in ng_con_part2(C)\n"); 13691acb27c6SJulian Elischer ng_destroy_hook(hook); /* also zaps peer */ 1370e088dd4cSAlexander Motin return (error); /* item was consumed. */ 13711acb27c6SJulian Elischer } 13726b795970SJulian Elischer hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */ 1373e088dd4cSAlexander Motin return (0); /* item was consumed. */ 1374e088dd4cSAlexander Motin done: 1375e088dd4cSAlexander Motin NG_FREE_ITEM(item); 1376e088dd4cSAlexander Motin return (error); 13774cf49a43SJulian Elischer } 13784cf49a43SJulian Elischer 13794cf49a43SJulian Elischer /* 13806b795970SJulian Elischer * Connect this node with another node. We assume that this node is 13816b795970SJulian Elischer * currently locked, as we are only called from an NGM_CONNECT message. 13824cf49a43SJulian Elischer */ 13836b795970SJulian Elischer static int 1384e088dd4cSAlexander Motin ng_con_nodes(item_p item, node_p node, const char *name, 1385e088dd4cSAlexander Motin node_p node2, const char *name2) 13864cf49a43SJulian Elischer { 13874cf49a43SJulian Elischer int error; 13884cf49a43SJulian Elischer hook_p hook; 13894cf49a43SJulian Elischer hook_p hook2; 13904cf49a43SJulian Elischer 13911acb27c6SJulian Elischer if (ng_findhook(node2, name2) != NULL) { 13921acb27c6SJulian Elischer return(EEXIST); 13931acb27c6SJulian Elischer } 13943e4084c8SJulian Elischer if ((error = ng_add_hook(node, name, &hook))) /* gives us a ref */ 13954cf49a43SJulian Elischer return (error); 13966b795970SJulian Elischer /* Allocate the other hook and link it up */ 13976b795970SJulian Elischer NG_ALLOC_HOOK(hook2); 139819724144SGleb Smirnoff if (hook2 == NULL) { 13996b795970SJulian Elischer TRAP_ERROR(); 14006b795970SJulian Elischer ng_destroy_hook(hook); /* XXX check ref counts so far */ 14016b795970SJulian Elischer NG_HOOK_UNREF(hook); /* including our ref */ 14026b795970SJulian Elischer return (ENOMEM); 14036b795970SJulian Elischer } 14046b795970SJulian Elischer hook2->hk_refs = 1; /* start with a reference for us. */ 14056b795970SJulian Elischer hook2->hk_flags = HK_INVALID; 14066b795970SJulian Elischer hook2->hk_peer = hook; /* Link the two together */ 14076b795970SJulian Elischer hook->hk_peer = hook2; 14086b795970SJulian Elischer NG_HOOK_REF(hook); /* Add a ref for the peer to each*/ 14096b795970SJulian Elischer NG_HOOK_REF(hook2); 14106b795970SJulian Elischer hook2->hk_node = &ng_deadnode; 141187e2c66aSHartmut Brandt strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ); 14126b795970SJulian Elischer 14136b795970SJulian Elischer /* 14146b795970SJulian Elischer * Queue the function above. 14156b795970SJulian Elischer * Procesing continues in that function in the lock context of 14166b795970SJulian Elischer * the other node. 14176b795970SJulian Elischer */ 1418b332b91fSGleb Smirnoff if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0, 1419b332b91fSGleb Smirnoff NG_NOFLAGS))) { 14203fb87c24SAlexander Motin printf("failed in ng_con_nodes(): %d\n", error); 14213fb87c24SAlexander Motin ng_destroy_hook(hook); /* also zaps peer */ 14223fb87c24SAlexander Motin } 14236b795970SJulian Elischer 14246b795970SJulian Elischer NG_HOOK_UNREF(hook); /* Let each hook go if it wants to */ 14256b795970SJulian Elischer NG_HOOK_UNREF(hook2); 14263fb87c24SAlexander Motin return (error); 14274cf49a43SJulian Elischer } 14286b795970SJulian Elischer 14296b795970SJulian Elischer /* 14306b795970SJulian Elischer * Make a peer and connect. 14316b795970SJulian Elischer * We assume that the local node is locked. 14326b795970SJulian Elischer * The new node probably doesn't need a lock until 14336b795970SJulian Elischer * it has a hook, because it cannot really have any work until then, 14346b795970SJulian Elischer * but we should think about it a bit more. 14356b795970SJulian Elischer * 14366b795970SJulian Elischer * The problem may come if the other node also fires up 14376b795970SJulian Elischer * some hardware or a timer or some other source of activation, 14386b795970SJulian Elischer * also it may already get a command msg via it's ID. 14396b795970SJulian Elischer * 14406b795970SJulian Elischer * We could use the same method as ng_con_nodes() but we'd have 14416b795970SJulian Elischer * to add ability to remove the node when failing. (Not hard, just 14426b795970SJulian Elischer * make arg1 point to the node to remove). 14436b795970SJulian Elischer * Unless of course we just ignore failure to connect and leave 14446b795970SJulian Elischer * an unconnected node? 14456b795970SJulian Elischer */ 14466b795970SJulian Elischer static int 14476b795970SJulian Elischer ng_mkpeer(node_p node, const char *name, const char *name2, char *type) 14486b795970SJulian Elischer { 14496b795970SJulian Elischer node_p node2; 14504c9b5910SGleb Smirnoff hook_p hook1, hook2; 14516b795970SJulian Elischer int error; 14526b795970SJulian Elischer 14536b795970SJulian Elischer if ((error = ng_make_node(type, &node2))) { 14546b795970SJulian Elischer return (error); 14556b795970SJulian Elischer } 14566b795970SJulian Elischer 14576b795970SJulian Elischer if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */ 14581acb27c6SJulian Elischer ng_rmnode(node2, NULL, NULL, 0); 14596b795970SJulian Elischer return (error); 14606b795970SJulian Elischer } 14616b795970SJulian Elischer 14626b795970SJulian Elischer if ((error = ng_add_hook(node2, name2, &hook2))) { 14631acb27c6SJulian Elischer ng_rmnode(node2, NULL, NULL, 0); 14646b795970SJulian Elischer ng_destroy_hook(hook1); 14656b795970SJulian Elischer NG_HOOK_UNREF(hook1); 14666b795970SJulian Elischer return (error); 14676b795970SJulian Elischer } 14686b795970SJulian Elischer 14696b795970SJulian Elischer /* 14706b795970SJulian Elischer * Actually link the two hooks together. 14716b795970SJulian Elischer */ 14726b795970SJulian Elischer hook1->hk_peer = hook2; 14736b795970SJulian Elischer hook2->hk_peer = hook1; 14746b795970SJulian Elischer 14756b795970SJulian Elischer /* Each hook is referenced by the other */ 14766b795970SJulian Elischer NG_HOOK_REF(hook1); 14776b795970SJulian Elischer NG_HOOK_REF(hook2); 14786b795970SJulian Elischer 14796b795970SJulian Elischer /* Give each node the opportunity to veto the pending connection */ 14806b795970SJulian Elischer if (hook1->hk_node->nd_type->connect) { 14816b795970SJulian Elischer error = (*hook1->hk_node->nd_type->connect) (hook1); 14826b795970SJulian Elischer } 14836b795970SJulian Elischer 14846b795970SJulian Elischer if ((error == 0) && hook2->hk_node->nd_type->connect) { 14856b795970SJulian Elischer error = (*hook2->hk_node->nd_type->connect) (hook2); 14866b795970SJulian Elischer 14876b795970SJulian Elischer } 14883e4084c8SJulian Elischer 14893e4084c8SJulian Elischer /* 14903e4084c8SJulian Elischer * drop the references we were holding on the two hooks. 14913e4084c8SJulian Elischer */ 14926b795970SJulian Elischer if (error) { 14936b795970SJulian Elischer ng_destroy_hook(hook2); /* also zaps hook1 */ 14941acb27c6SJulian Elischer ng_rmnode(node2, NULL, NULL, 0); 14956b795970SJulian Elischer } else { 14966b795970SJulian Elischer /* As a last act, allow the hooks to be used */ 14976b795970SJulian Elischer hook1->hk_flags &= ~HK_INVALID; 14986b795970SJulian Elischer hook2->hk_flags &= ~HK_INVALID; 14996b795970SJulian Elischer } 15006b795970SJulian Elischer NG_HOOK_UNREF(hook1); 15013e4084c8SJulian Elischer NG_HOOK_UNREF(hook2); 15023e4084c8SJulian Elischer return (error); 15034cf49a43SJulian Elischer } 15046b795970SJulian Elischer 1505069154d5SJulian Elischer /************************************************************************ 1506069154d5SJulian Elischer Utility routines to send self messages 1507069154d5SJulian Elischer ************************************************************************/ 1508069154d5SJulian Elischer 15091acb27c6SJulian Elischer /* Shut this node down as soon as everyone is clear of it */ 151064efc707SRobert Watson /* Should add arg "immediately" to jump the queue */ 1511069154d5SJulian Elischer int 15121acb27c6SJulian Elischer ng_rmnode_self(node_p node) 1513069154d5SJulian Elischer { 15141acb27c6SJulian Elischer int error; 15154cf49a43SJulian Elischer 15161acb27c6SJulian Elischer if (node == &ng_deadnode) 15171acb27c6SJulian Elischer return (0); 1518be4252b3SJulian Elischer node->nd_flags |= NGF_INVALID; 1519be4252b3SJulian Elischer if (node->nd_flags & NGF_CLOSING) 15201acb27c6SJulian Elischer return (0); 1521069154d5SJulian Elischer 15221acb27c6SJulian Elischer error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0); 15231acb27c6SJulian Elischer return (error); 1524069154d5SJulian Elischer } 1525069154d5SJulian Elischer 15261acb27c6SJulian Elischer static void 15276b795970SJulian Elischer ng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2) 15286b795970SJulian Elischer { 15296b795970SJulian Elischer ng_destroy_hook(hook); 15301acb27c6SJulian Elischer return ; 15316b795970SJulian Elischer } 15326b795970SJulian Elischer 1533954c4772SJulian Elischer int 1534954c4772SJulian Elischer ng_rmhook_self(hook_p hook) 1535954c4772SJulian Elischer { 15366b795970SJulian Elischer int error; 1537954c4772SJulian Elischer node_p node = NG_HOOK_NODE(hook); 1538954c4772SJulian Elischer 15396b795970SJulian Elischer if (node == &ng_deadnode) 15406b795970SJulian Elischer return (0); 15416b795970SJulian Elischer 15426b795970SJulian Elischer error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0); 15436b795970SJulian Elischer return (error); 1544954c4772SJulian Elischer } 1545954c4772SJulian Elischer 1546069154d5SJulian Elischer /*********************************************************************** 15474cf49a43SJulian Elischer * Parse and verify a string of the form: <NODE:><PATH> 15484cf49a43SJulian Elischer * 15494cf49a43SJulian Elischer * Such a string can refer to a specific node or a specific hook 15504cf49a43SJulian Elischer * on a specific node, depending on how you look at it. In the 15514cf49a43SJulian Elischer * latter case, the PATH component must not end in a dot. 15524cf49a43SJulian Elischer * 15534cf49a43SJulian Elischer * Both <NODE:> and <PATH> are optional. The <PATH> is a string 15544cf49a43SJulian Elischer * of hook names separated by dots. This breaks out the original 15554cf49a43SJulian Elischer * string, setting *nodep to "NODE" (or NULL if none) and *pathp 15564cf49a43SJulian Elischer * to "PATH" (or NULL if degenerate). Also, *hookp will point to 15574cf49a43SJulian Elischer * the final hook component of <PATH>, if any, otherwise NULL. 15584cf49a43SJulian Elischer * 15594cf49a43SJulian Elischer * This returns -1 if the path is malformed. The char ** are optional. 1560069154d5SJulian Elischer ***********************************************************************/ 15614cf49a43SJulian Elischer int 15624cf49a43SJulian Elischer ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 15634cf49a43SJulian Elischer { 15644cf49a43SJulian Elischer char *node, *path, *hook; 15654cf49a43SJulian Elischer int k; 15664cf49a43SJulian Elischer 15674cf49a43SJulian Elischer /* 15684cf49a43SJulian Elischer * Extract absolute NODE, if any 15694cf49a43SJulian Elischer */ 15704cf49a43SJulian Elischer for (path = addr; *path && *path != ':'; path++); 15714cf49a43SJulian Elischer if (*path) { 15724cf49a43SJulian Elischer node = addr; /* Here's the NODE */ 15734cf49a43SJulian Elischer *path++ = '\0'; /* Here's the PATH */ 15744cf49a43SJulian Elischer 15754cf49a43SJulian Elischer /* Node name must not be empty */ 15764cf49a43SJulian Elischer if (!*node) 15774cf49a43SJulian Elischer return -1; 15784cf49a43SJulian Elischer 15794cf49a43SJulian Elischer /* A name of "." is OK; otherwise '.' not allowed */ 15804cf49a43SJulian Elischer if (strcmp(node, ".") != 0) { 15814cf49a43SJulian Elischer for (k = 0; node[k]; k++) 15824cf49a43SJulian Elischer if (node[k] == '.') 15834cf49a43SJulian Elischer return -1; 15844cf49a43SJulian Elischer } 15854cf49a43SJulian Elischer } else { 15864cf49a43SJulian Elischer node = NULL; /* No absolute NODE */ 15874cf49a43SJulian Elischer path = addr; /* Here's the PATH */ 15884cf49a43SJulian Elischer } 15894cf49a43SJulian Elischer 15904cf49a43SJulian Elischer /* Snoop for illegal characters in PATH */ 15914cf49a43SJulian Elischer for (k = 0; path[k]; k++) 15924cf49a43SJulian Elischer if (path[k] == ':') 15934cf49a43SJulian Elischer return -1; 15944cf49a43SJulian Elischer 15954cf49a43SJulian Elischer /* Check for no repeated dots in PATH */ 15964cf49a43SJulian Elischer for (k = 0; path[k]; k++) 15974cf49a43SJulian Elischer if (path[k] == '.' && path[k + 1] == '.') 15984cf49a43SJulian Elischer return -1; 15994cf49a43SJulian Elischer 16004cf49a43SJulian Elischer /* Remove extra (degenerate) dots from beginning or end of PATH */ 16014cf49a43SJulian Elischer if (path[0] == '.') 16024cf49a43SJulian Elischer path++; 16034cf49a43SJulian Elischer if (*path && path[strlen(path) - 1] == '.') 16044cf49a43SJulian Elischer path[strlen(path) - 1] = 0; 16054cf49a43SJulian Elischer 16064cf49a43SJulian Elischer /* If PATH has a dot, then we're not talking about a hook */ 16074cf49a43SJulian Elischer if (*path) { 16084cf49a43SJulian Elischer for (hook = path, k = 0; path[k]; k++) 16094cf49a43SJulian Elischer if (path[k] == '.') { 16104cf49a43SJulian Elischer hook = NULL; 16114cf49a43SJulian Elischer break; 16124cf49a43SJulian Elischer } 16134cf49a43SJulian Elischer } else 16144cf49a43SJulian Elischer path = hook = NULL; 16154cf49a43SJulian Elischer 16164cf49a43SJulian Elischer /* Done */ 16174cf49a43SJulian Elischer if (nodep) 16184cf49a43SJulian Elischer *nodep = node; 16194cf49a43SJulian Elischer if (pathp) 16204cf49a43SJulian Elischer *pathp = path; 16214cf49a43SJulian Elischer if (hookp) 16224cf49a43SJulian Elischer *hookp = hook; 16234cf49a43SJulian Elischer return (0); 16244cf49a43SJulian Elischer } 16254cf49a43SJulian Elischer 16264cf49a43SJulian Elischer /* 16274cf49a43SJulian Elischer * Given a path, which may be absolute or relative, and a starting node, 1628069154d5SJulian Elischer * return the destination node. 16294cf49a43SJulian Elischer */ 16304cf49a43SJulian Elischer int 1631069154d5SJulian Elischer ng_path2noderef(node_p here, const char *address, 1632069154d5SJulian Elischer node_p *destp, hook_p *lasthook) 16334cf49a43SJulian Elischer { 163487e2c66aSHartmut Brandt char fullpath[NG_PATHSIZ]; 16354cf49a43SJulian Elischer char *nodename, *path, pbuf[2]; 1636069154d5SJulian Elischer node_p node, oldnode; 16374cf49a43SJulian Elischer char *cp; 1638a4ec03cfSJulian Elischer hook_p hook = NULL; 16394cf49a43SJulian Elischer 16404cf49a43SJulian Elischer /* Initialize */ 164130400f03SJulian Elischer if (destp == NULL) { 16426b795970SJulian Elischer TRAP_ERROR(); 16434cf49a43SJulian Elischer return EINVAL; 164430400f03SJulian Elischer } 16454cf49a43SJulian Elischer *destp = NULL; 16464cf49a43SJulian Elischer 16474cf49a43SJulian Elischer /* Make a writable copy of address for ng_path_parse() */ 16484cf49a43SJulian Elischer strncpy(fullpath, address, sizeof(fullpath) - 1); 16494cf49a43SJulian Elischer fullpath[sizeof(fullpath) - 1] = '\0'; 16504cf49a43SJulian Elischer 16514cf49a43SJulian Elischer /* Parse out node and sequence of hooks */ 16524cf49a43SJulian Elischer if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 16536b795970SJulian Elischer TRAP_ERROR(); 16544cf49a43SJulian Elischer return EINVAL; 16554cf49a43SJulian Elischer } 16564cf49a43SJulian Elischer if (path == NULL) { 16574cf49a43SJulian Elischer pbuf[0] = '.'; /* Needs to be writable */ 16584cf49a43SJulian Elischer pbuf[1] = '\0'; 16594cf49a43SJulian Elischer path = pbuf; 16604cf49a43SJulian Elischer } 16614cf49a43SJulian Elischer 1662069154d5SJulian Elischer /* 1663069154d5SJulian Elischer * For an absolute address, jump to the starting node. 1664069154d5SJulian Elischer * Note that this holds a reference on the node for us. 1665069154d5SJulian Elischer * Don't forget to drop the reference if we don't need it. 1666069154d5SJulian Elischer */ 16674cf49a43SJulian Elischer if (nodename) { 1668069154d5SJulian Elischer node = ng_name2noderef(here, nodename); 16694cf49a43SJulian Elischer if (node == NULL) { 16706b795970SJulian Elischer TRAP_ERROR(); 16714cf49a43SJulian Elischer return (ENOENT); 16724cf49a43SJulian Elischer } 1673069154d5SJulian Elischer } else { 1674069154d5SJulian Elischer if (here == NULL) { 16756b795970SJulian Elischer TRAP_ERROR(); 1676069154d5SJulian Elischer return (EINVAL); 1677069154d5SJulian Elischer } 16784cf49a43SJulian Elischer node = here; 167930400f03SJulian Elischer NG_NODE_REF(node); 1680069154d5SJulian Elischer } 16814cf49a43SJulian Elischer 1682069154d5SJulian Elischer /* 1683069154d5SJulian Elischer * Now follow the sequence of hooks 1684069154d5SJulian Elischer * XXX 1685069154d5SJulian Elischer * We actually cannot guarantee that the sequence 1686069154d5SJulian Elischer * is not being demolished as we crawl along it 1687069154d5SJulian Elischer * without extra-ordinary locking etc. 1688069154d5SJulian Elischer * So this is a bit dodgy to say the least. 1689069154d5SJulian Elischer * We can probably hold up some things by holding 1690069154d5SJulian Elischer * the nodelist mutex for the time of this 1691069154d5SJulian Elischer * crawl if we wanted.. At least that way we wouldn't have to 169264efc707SRobert Watson * worry about the nodes disappearing, but the hooks would still 1693069154d5SJulian Elischer * be a problem. 1694069154d5SJulian Elischer */ 16954cf49a43SJulian Elischer for (cp = path; node != NULL && *cp != '\0'; ) { 16964cf49a43SJulian Elischer char *segment; 16974cf49a43SJulian Elischer 16984cf49a43SJulian Elischer /* 16994cf49a43SJulian Elischer * Break out the next path segment. Replace the dot we just 17004cf49a43SJulian Elischer * found with a NUL; "cp" points to the next segment (or the 17014cf49a43SJulian Elischer * NUL at the end). 17024cf49a43SJulian Elischer */ 17034cf49a43SJulian Elischer for (segment = cp; *cp != '\0'; cp++) { 17044cf49a43SJulian Elischer if (*cp == '.') { 17054cf49a43SJulian Elischer *cp++ = '\0'; 17064cf49a43SJulian Elischer break; 17074cf49a43SJulian Elischer } 17084cf49a43SJulian Elischer } 17094cf49a43SJulian Elischer 17104cf49a43SJulian Elischer /* Empty segment */ 17114cf49a43SJulian Elischer if (*segment == '\0') 17124cf49a43SJulian Elischer continue; 17134cf49a43SJulian Elischer 17144cf49a43SJulian Elischer /* We have a segment, so look for a hook by that name */ 1715899e9c4eSArchie Cobbs hook = ng_findhook(node, segment); 17164cf49a43SJulian Elischer 17174cf49a43SJulian Elischer /* Can't get there from here... */ 17184cf49a43SJulian Elischer if (hook == NULL 171930400f03SJulian Elischer || NG_HOOK_PEER(hook) == NULL 172030400f03SJulian Elischer || NG_HOOK_NOT_VALID(hook) 172130400f03SJulian Elischer || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) { 17226b795970SJulian Elischer TRAP_ERROR(); 172330400f03SJulian Elischer NG_NODE_UNREF(node); 172430400f03SJulian Elischer #if 0 172530400f03SJulian Elischer printf("hooknotvalid %s %s %d %d %d %d ", 172630400f03SJulian Elischer path, 172730400f03SJulian Elischer segment, 172830400f03SJulian Elischer hook == NULL, 172930400f03SJulian Elischer NG_HOOK_PEER(hook) == NULL, 173030400f03SJulian Elischer NG_HOOK_NOT_VALID(hook), 173130400f03SJulian Elischer NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))); 173230400f03SJulian Elischer #endif 17334cf49a43SJulian Elischer return (ENOENT); 17344cf49a43SJulian Elischer } 17354cf49a43SJulian Elischer 1736069154d5SJulian Elischer /* 1737069154d5SJulian Elischer * Hop on over to the next node 1738069154d5SJulian Elischer * XXX 1739069154d5SJulian Elischer * Big race conditions here as hooks and nodes go away 1740069154d5SJulian Elischer * *** Idea.. store an ng_ID_t in each hook and use that 1741069154d5SJulian Elischer * instead of the direct hook in this crawl? 1742069154d5SJulian Elischer */ 1743069154d5SJulian Elischer oldnode = node; 174430400f03SJulian Elischer if ((node = NG_PEER_NODE(hook))) 174530400f03SJulian Elischer NG_NODE_REF(node); /* XXX RACE */ 174630400f03SJulian Elischer NG_NODE_UNREF(oldnode); /* XXX another race */ 174730400f03SJulian Elischer if (NG_NODE_NOT_VALID(node)) { 174830400f03SJulian Elischer NG_NODE_UNREF(node); /* XXX more races */ 1749069154d5SJulian Elischer node = NULL; 1750069154d5SJulian Elischer } 17514cf49a43SJulian Elischer } 17524cf49a43SJulian Elischer 17534cf49a43SJulian Elischer /* If node somehow missing, fail here (probably this is not needed) */ 17544cf49a43SJulian Elischer if (node == NULL) { 17556b795970SJulian Elischer TRAP_ERROR(); 17564cf49a43SJulian Elischer return (ENXIO); 17574cf49a43SJulian Elischer } 17584cf49a43SJulian Elischer 17594cf49a43SJulian Elischer /* Done */ 17604cf49a43SJulian Elischer *destp = node; 17611bdebe4dSArchie Cobbs if (lasthook != NULL) 176230400f03SJulian Elischer *lasthook = (hook ? NG_HOOK_PEER(hook) : NULL); 1763069154d5SJulian Elischer return (0); 1764069154d5SJulian Elischer } 1765069154d5SJulian Elischer 1766069154d5SJulian Elischer /***************************************************************\ 1767069154d5SJulian Elischer * Input queue handling. 1768069154d5SJulian Elischer * All activities are submitted to the node via the input queue 1769069154d5SJulian Elischer * which implements a multiple-reader/single-writer gate. 177064efc707SRobert Watson * Items which cannot be handled immediately are queued. 1771069154d5SJulian Elischer * 1772069154d5SJulian Elischer * read-write queue locking inline functions * 1773069154d5SJulian Elischer \***************************************************************/ 1774069154d5SJulian Elischer 1775714fb865SGleb Smirnoff static __inline item_p ng_dequeue(struct ng_queue * ngq, int *rw); 1776069154d5SJulian Elischer static __inline item_p ng_acquire_read(struct ng_queue * ngq, 1777069154d5SJulian Elischer item_p item); 1778069154d5SJulian Elischer static __inline item_p ng_acquire_write(struct ng_queue * ngq, 1779069154d5SJulian Elischer item_p item); 1780069154d5SJulian Elischer static __inline void ng_leave_read(struct ng_queue * ngq); 1781069154d5SJulian Elischer static __inline void ng_leave_write(struct ng_queue * ngq); 1782069154d5SJulian Elischer static __inline void ng_queue_rw(struct ng_queue * ngq, 1783069154d5SJulian Elischer item_p item, int rw); 1784069154d5SJulian Elischer 1785069154d5SJulian Elischer /* 1786069154d5SJulian Elischer * Definition of the bits fields in the ng_queue flag word. 1787069154d5SJulian Elischer * Defined here rather than in netgraph.h because no-one should fiddle 1788069154d5SJulian Elischer * with them. 1789069154d5SJulian Elischer * 1790b57a7965SJulian Elischer * The ordering here may be important! don't shuffle these. 1791069154d5SJulian Elischer */ 1792069154d5SJulian Elischer /*- 1793069154d5SJulian Elischer Safety Barrier--------+ (adjustable to suit taste) (not used yet) 1794069154d5SJulian Elischer | 1795069154d5SJulian Elischer V 1796069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+ 17974be59335SGleb Smirnoff | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 17984be59335SGleb Smirnoff | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A| 17994be59335SGleb Smirnoff | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W| 1800069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+ 18014be59335SGleb Smirnoff \___________________________ ____________________________/ | | 18024be59335SGleb Smirnoff V | | 18034be59335SGleb Smirnoff [active reader count] | | 1804069154d5SJulian Elischer | | 18054be59335SGleb Smirnoff Operation Pending -------------------------------+ | 1806069154d5SJulian Elischer | 18074be59335SGleb Smirnoff Active Writer ---------------------------------------+ 1808b57a7965SJulian Elischer 18098f9ac44aSAlexander Motin Node queue has such semantics: 18108f9ac44aSAlexander Motin - All flags modifications are atomic. 18118f9ac44aSAlexander Motin - Reader count can be incremented only if there is no writer or pending flags. 18128f9ac44aSAlexander Motin As soon as this can't be done with single operation, it is implemented with 18138f9ac44aSAlexander Motin spin loop and atomic_cmpset(). 18148f9ac44aSAlexander Motin - Writer flag can be set only if there is no any bits set. 18158f9ac44aSAlexander Motin It is implemented with atomic_cmpset(). 18168f9ac44aSAlexander Motin - Pending flag can be set any time, but to avoid collision on queue processing 18178f9ac44aSAlexander Motin all queue fields are protected by the mutex. 18188f9ac44aSAlexander Motin - Queue processing thread reads queue holding the mutex, but releases it while 18198f9ac44aSAlexander Motin processing. When queue is empty pending flag is removed. 1820069154d5SJulian Elischer */ 18218f9ac44aSAlexander Motin 18224be59335SGleb Smirnoff #define WRITER_ACTIVE 0x00000001 18234be59335SGleb Smirnoff #define OP_PENDING 0x00000002 18244be59335SGleb Smirnoff #define READER_INCREMENT 0x00000004 18254be59335SGleb Smirnoff #define READER_MASK 0xfffffffc /* Not valid if WRITER_ACTIVE is set */ 18264be59335SGleb Smirnoff #define SAFETY_BARRIER 0x00100000 /* 128K items queued should be enough */ 1827b57a7965SJulian Elischer 1828b57a7965SJulian Elischer /* Defines of more elaborate states on the queue */ 18294be59335SGleb Smirnoff /* Mask of bits a new read cares about */ 18304be59335SGleb Smirnoff #define NGQ_RMASK (WRITER_ACTIVE|OP_PENDING) 1831b57a7965SJulian Elischer 18324be59335SGleb Smirnoff /* Mask of bits a new write cares about */ 1833b57a7965SJulian Elischer #define NGQ_WMASK (NGQ_RMASK|READER_MASK) 1834b57a7965SJulian Elischer 18354be59335SGleb Smirnoff /* Test to decide if there is something on the queue. */ 18364be59335SGleb Smirnoff #define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING) 18374be59335SGleb Smirnoff 18384be59335SGleb Smirnoff /* How to decide what the next queued item is. */ 18394be59335SGleb Smirnoff #define HEAD_IS_READER(QP) NGI_QUEUED_READER((QP)->queue) 18404be59335SGleb Smirnoff #define HEAD_IS_WRITER(QP) NGI_QUEUED_WRITER((QP)->queue) /* notused */ 18414be59335SGleb Smirnoff 18424be59335SGleb Smirnoff /* Read the status to decide if the next item on the queue can now run. */ 18434be59335SGleb Smirnoff #define QUEUED_READER_CAN_PROCEED(QP) \ 18444be59335SGleb Smirnoff (((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0) 18454be59335SGleb Smirnoff #define QUEUED_WRITER_CAN_PROCEED(QP) \ 18464be59335SGleb Smirnoff (((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0) 1847b57a7965SJulian Elischer 1848b57a7965SJulian Elischer /* Is there a chance of getting ANY work off the queue? */ 18494be59335SGleb Smirnoff #define NEXT_QUEUED_ITEM_CAN_PROCEED(QP) \ 18504be59335SGleb Smirnoff ((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) : \ 1851394cb30aSAlexander Motin QUEUED_WRITER_CAN_PROCEED(QP)) 18524be59335SGleb Smirnoff 1853b57a7965SJulian Elischer 1854714fb865SGleb Smirnoff #define NGQRW_R 0 1855714fb865SGleb Smirnoff #define NGQRW_W 1 1856714fb865SGleb Smirnoff 1857069154d5SJulian Elischer /* 1858069154d5SJulian Elischer * Taking into account the current state of the queue and node, possibly take 1859069154d5SJulian Elischer * the next entry off the queue and return it. Return NULL if there was 1860069154d5SJulian Elischer * nothing we could return, either because there really was nothing there, or 1861069154d5SJulian Elischer * because the node was in a state where it cannot yet process the next item 1862069154d5SJulian Elischer * on the queue. 1863069154d5SJulian Elischer */ 1864069154d5SJulian Elischer static __inline item_p 1865714fb865SGleb Smirnoff ng_dequeue(struct ng_queue *ngq, int *rw) 1866069154d5SJulian Elischer { 1867069154d5SJulian Elischer item_p item; 1868b57a7965SJulian Elischer 18698f9ac44aSAlexander Motin /* This MUST be called with the mutex held. */ 1870d58e678fSGleb Smirnoff mtx_assert(&ngq->q_mtx, MA_OWNED); 18718f9ac44aSAlexander Motin 18728f9ac44aSAlexander Motin /* If there is nothing queued, then just return. */ 18734be59335SGleb Smirnoff if (!QUEUE_ACTIVE(ngq)) { 18742955ee18SGleb Smirnoff CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; " 18752955ee18SGleb Smirnoff "queue flags 0x%lx", __func__, 18762955ee18SGleb Smirnoff ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags); 18774be59335SGleb Smirnoff return (NULL); 18784be59335SGleb Smirnoff } 1879d58e678fSGleb Smirnoff 18804be59335SGleb Smirnoff /* 18814be59335SGleb Smirnoff * From here, we can assume there is a head item. 18824be59335SGleb Smirnoff * We need to find out what it is and if it can be dequeued, given 18834be59335SGleb Smirnoff * the current state of the node. 18844be59335SGleb Smirnoff */ 18854be59335SGleb Smirnoff if (HEAD_IS_READER(ngq)) { 1886394cb30aSAlexander Motin while (1) { 1887394cb30aSAlexander Motin long t = ngq->q_flags; 1888394cb30aSAlexander Motin if (t & WRITER_ACTIVE) { 18898f9ac44aSAlexander Motin /* There is writer, reader can't proceed. */ 18902955ee18SGleb Smirnoff CTR4(KTR_NET, "%20s: node [%x] (%p) queued reader " 18912955ee18SGleb Smirnoff "can't proceed; queue flags 0x%lx", __func__, 1892394cb30aSAlexander Motin ngq->q_node->nd_ID, ngq->q_node, t); 18934be59335SGleb Smirnoff return (NULL); 18944be59335SGleb Smirnoff } 18958f9ac44aSAlexander Motin if (atomic_cmpset_acq_long(&ngq->q_flags, t, 1896394cb30aSAlexander Motin t + READER_INCREMENT)) 1897394cb30aSAlexander Motin break; 1898394cb30aSAlexander Motin cpu_spinwait(); 1899394cb30aSAlexander Motin } 19008f9ac44aSAlexander Motin /* We have got reader lock for the node. */ 1901714fb865SGleb Smirnoff *rw = NGQRW_R; 19028f9ac44aSAlexander Motin } else if (atomic_cmpset_acq_long(&ngq->q_flags, OP_PENDING, 1903394cb30aSAlexander Motin OP_PENDING + WRITER_ACTIVE)) { 19048f9ac44aSAlexander Motin /* We have got writer lock for the node. */ 1905714fb865SGleb Smirnoff *rw = NGQRW_W; 1906069154d5SJulian Elischer } else { 19078f9ac44aSAlexander Motin /* There is somebody other, writer can't proceed. */ 1908394cb30aSAlexander Motin CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer " 1909394cb30aSAlexander Motin "can't proceed; queue flags 0x%lx", __func__, 19102955ee18SGleb Smirnoff ngq->q_node->nd_ID, ngq->q_node, ngq->q_flags); 19114be59335SGleb Smirnoff return (NULL); 19124cf49a43SJulian Elischer } 19134cf49a43SJulian Elischer 19144cf49a43SJulian Elischer /* 1915069154d5SJulian Elischer * Now we dequeue the request (whatever it may be) and correct the 1916069154d5SJulian Elischer * pending flags and the next and last pointers. 19174cf49a43SJulian Elischer */ 1918069154d5SJulian Elischer item = ngq->queue; 1919069154d5SJulian Elischer ngq->queue = item->el_next; 1920069154d5SJulian Elischer if (ngq->last == &(item->el_next)) { 1921069154d5SJulian Elischer ngq->last = &(ngq->queue); 1922394cb30aSAlexander Motin atomic_clear_long(&ngq->q_flags, OP_PENDING); 1923b57a7965SJulian Elischer } 19242955ee18SGleb Smirnoff CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; " 19252955ee18SGleb Smirnoff "queue flags 0x%lx", __func__, 19262955ee18SGleb Smirnoff ngq->q_node->nd_ID, ngq->q_node, item, *rw ? "WRITER" : "READER" , 19273b33fbe7SGleb Smirnoff ngq->q_flags); 1928069154d5SJulian Elischer return (item); 1929069154d5SJulian Elischer } 1930859a4d16SJulian Elischer 1931859a4d16SJulian Elischer /* 19328f9ac44aSAlexander Motin * Queue a packet to be picked up later by someone else. 19338f9ac44aSAlexander Motin * If the queue could be run now, add node to the queue handler's worklist. 1934859a4d16SJulian Elischer */ 1935069154d5SJulian Elischer static __inline void 1936069154d5SJulian Elischer ng_queue_rw(struct ng_queue * ngq, item_p item, int rw) 1937069154d5SJulian Elischer { 19384be59335SGleb Smirnoff if (rw == NGQRW_W) 19394be59335SGleb Smirnoff NGI_SET_WRITER(item); 19404be59335SGleb Smirnoff else 19414be59335SGleb Smirnoff NGI_SET_READER(item); 1942069154d5SJulian Elischer item->el_next = NULL; /* maybe not needed */ 1943394cb30aSAlexander Motin 1944394cb30aSAlexander Motin NG_QUEUE_LOCK(ngq); 1945394cb30aSAlexander Motin /* Set OP_PENDING flag and enqueue the item. */ 1946394cb30aSAlexander Motin atomic_set_long(&ngq->q_flags, OP_PENDING); 1947069154d5SJulian Elischer *ngq->last = item; 1948394cb30aSAlexander Motin ngq->last = &(item->el_next); 1949394cb30aSAlexander Motin 19502955ee18SGleb Smirnoff CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__, 19512955ee18SGleb Smirnoff ngq->q_node->nd_ID, ngq->q_node, item, rw ? "WRITER" : "READER" ); 19524be59335SGleb Smirnoff 19534be59335SGleb Smirnoff /* 19544be59335SGleb Smirnoff * We can take the worklist lock with the node locked 19554be59335SGleb Smirnoff * BUT NOT THE REVERSE! 19564be59335SGleb Smirnoff */ 19574be59335SGleb Smirnoff if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 1958394cb30aSAlexander Motin ng_worklist_add(ngq->q_node); 1959394cb30aSAlexander Motin NG_QUEUE_UNLOCK(ngq); 1960069154d5SJulian Elischer } 1961069154d5SJulian Elischer 19628f9ac44aSAlexander Motin /* Acquire reader lock on node. If node is busy, queue the packet. */ 1963069154d5SJulian Elischer static __inline item_p 1964069154d5SJulian Elischer ng_acquire_read(struct ng_queue *ngq, item_p item) 1965069154d5SJulian Elischer { 1966ac5dd141SGleb Smirnoff KASSERT(ngq != &ng_deadnode.nd_input_queue, 1967ac5dd141SGleb Smirnoff ("%s: working on deadnode", __func__)); 1968069154d5SJulian Elischer 1969394cb30aSAlexander Motin /* Reader needs node without writer and pending items. */ 1970394cb30aSAlexander Motin while (1) { 1971394cb30aSAlexander Motin long t = ngq->q_flags; 1972394cb30aSAlexander Motin if (t & NGQ_RMASK) 1973394cb30aSAlexander Motin break; /* Node is not ready for reader. */ 19748f9ac44aSAlexander Motin if (atomic_cmpset_acq_long(&ngq->q_flags, t, t + READER_INCREMENT)) { 1975069154d5SJulian Elischer /* Successfully grabbed node */ 1976394cb30aSAlexander Motin CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", 19772955ee18SGleb Smirnoff __func__, ngq->q_node->nd_ID, ngq->q_node, item); 1978069154d5SJulian Elischer return (item); 1979069154d5SJulian Elischer } 1980394cb30aSAlexander Motin cpu_spinwait(); 1981394cb30aSAlexander Motin }; 1982069154d5SJulian Elischer 1983394cb30aSAlexander Motin /* Queue the request for later. */ 1984069154d5SJulian Elischer ng_queue_rw(ngq, item, NGQRW_R); 1985f912c0f7SGleb Smirnoff 1986f912c0f7SGleb Smirnoff return (NULL); 1987069154d5SJulian Elischer } 1988069154d5SJulian Elischer 19898f9ac44aSAlexander Motin /* Acquire writer lock on node. If node is busy, queue the packet. */ 1990069154d5SJulian Elischer static __inline item_p 1991069154d5SJulian Elischer ng_acquire_write(struct ng_queue *ngq, item_p item) 1992069154d5SJulian Elischer { 1993ac5dd141SGleb Smirnoff KASSERT(ngq != &ng_deadnode.nd_input_queue, 1994ac5dd141SGleb Smirnoff ("%s: working on deadnode", __func__)); 1995ac5dd141SGleb Smirnoff 1996394cb30aSAlexander Motin /* Writer needs completely idle node. */ 19978f9ac44aSAlexander Motin if (atomic_cmpset_acq_long(&ngq->q_flags, 0, WRITER_ACTIVE)) { 1998394cb30aSAlexander Motin /* Successfully grabbed node */ 19992955ee18SGleb Smirnoff CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", 20002955ee18SGleb Smirnoff __func__, ngq->q_node->nd_ID, ngq->q_node, item); 2001069154d5SJulian Elischer return (item); 2002069154d5SJulian Elischer } 2003069154d5SJulian Elischer 2004394cb30aSAlexander Motin /* Queue the request for later. */ 2005069154d5SJulian Elischer ng_queue_rw(ngq, item, NGQRW_W); 2006f912c0f7SGleb Smirnoff 2007f912c0f7SGleb Smirnoff return (NULL); 2008069154d5SJulian Elischer } 2009069154d5SJulian Elischer 2010262dfd7fSJulian Elischer #if 0 2011262dfd7fSJulian Elischer static __inline item_p 2012262dfd7fSJulian Elischer ng_upgrade_write(struct ng_queue *ngq, item_p item) 2013262dfd7fSJulian Elischer { 2014262dfd7fSJulian Elischer KASSERT(ngq != &ng_deadnode.nd_input_queue, 2015262dfd7fSJulian Elischer ("%s: working on deadnode", __func__)); 2016262dfd7fSJulian Elischer 2017262dfd7fSJulian Elischer NGI_SET_WRITER(item); 2018262dfd7fSJulian Elischer 2019262dfd7fSJulian Elischer mtx_lock_spin(&(ngq->q_mtx)); 2020262dfd7fSJulian Elischer 2021262dfd7fSJulian Elischer /* 2022262dfd7fSJulian Elischer * There will never be no readers as we are there ourselves. 2023262dfd7fSJulian Elischer * Set the WRITER_ACTIVE flags ASAP to block out fast track readers. 2024262dfd7fSJulian Elischer * The caller we are running from will call ng_leave_read() 2025262dfd7fSJulian Elischer * soon, so we must account for that. We must leave again with the 2026262dfd7fSJulian Elischer * READER lock. If we find other readers, then 2027262dfd7fSJulian Elischer * queue the request for later. However "later" may be rignt now 2028262dfd7fSJulian Elischer * if there are no readers. We don't really care if there are queued 2029262dfd7fSJulian Elischer * items as we will bypass them anyhow. 2030262dfd7fSJulian Elischer */ 2031262dfd7fSJulian Elischer atomic_add_long(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT); 2032262dfd7fSJulian Elischer if (ngq->q_flags & (NGQ_WMASK & ~OP_PENDING) == WRITER_ACTIVE) { 2033262dfd7fSJulian Elischer mtx_unlock_spin(&(ngq->q_mtx)); 2034262dfd7fSJulian Elischer 2035262dfd7fSJulian Elischer /* It's just us, act on the item. */ 2036262dfd7fSJulian Elischer /* will NOT drop writer lock when done */ 2037262dfd7fSJulian Elischer ng_apply_item(node, item, 0); 2038262dfd7fSJulian Elischer 2039262dfd7fSJulian Elischer /* 2040262dfd7fSJulian Elischer * Having acted on the item, atomically 2041262dfd7fSJulian Elischer * down grade back to READER and finish up 2042262dfd7fSJulian Elischer */ 2043262dfd7fSJulian Elischer atomic_add_long(&ngq->q_flags, 2044262dfd7fSJulian Elischer READER_INCREMENT - WRITER_ACTIVE); 2045262dfd7fSJulian Elischer 2046262dfd7fSJulian Elischer /* Our caller will call ng_leave_read() */ 2047262dfd7fSJulian Elischer return; 2048262dfd7fSJulian Elischer } 2049262dfd7fSJulian Elischer /* 2050262dfd7fSJulian Elischer * It's not just us active, so queue us AT THE HEAD. 2051262dfd7fSJulian Elischer * "Why?" I hear you ask. 2052262dfd7fSJulian Elischer * Put us at the head of the queue as we've already been 2053262dfd7fSJulian Elischer * through it once. If there is nothing else waiting, 2054262dfd7fSJulian Elischer * set the correct flags. 2055262dfd7fSJulian Elischer */ 2056262dfd7fSJulian Elischer if ((item->el_next = ngq->queue) == NULL) { 2057262dfd7fSJulian Elischer /* 2058262dfd7fSJulian Elischer * Set up the "last" pointer. 2059262dfd7fSJulian Elischer * We are the only (and thus last) item 2060262dfd7fSJulian Elischer */ 2061262dfd7fSJulian Elischer ngq->last = &(item->el_next); 2062262dfd7fSJulian Elischer 2063262dfd7fSJulian Elischer /* We've gone from, 0 to 1 item in the queue */ 2064394cb30aSAlexander Motin atomic_set_long(&ngq->q_flags, OP_PENDING); 2065262dfd7fSJulian Elischer 2066262dfd7fSJulian Elischer CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__, 2067262dfd7fSJulian Elischer ngq->q_node->nd_ID, ngq->q_node); 2068262dfd7fSJulian Elischer }; 2069262dfd7fSJulian Elischer ngq->queue = item; 2070262dfd7fSJulian Elischer CTR5(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER", 2071262dfd7fSJulian Elischer __func__, ngq->q_node->nd_ID, ngq->q_node, item ); 2072262dfd7fSJulian Elischer 2073262dfd7fSJulian Elischer /* Reverse what we did above. That downgrades us back to reader */ 2074262dfd7fSJulian Elischer atomic_add_long(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE); 2075394cb30aSAlexander Motin if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2076394cb30aSAlexander Motin ng_worklist_add(ngq->q_node); 2077262dfd7fSJulian Elischer mtx_unlock_spin(&(ngq->q_mtx)); 2078262dfd7fSJulian Elischer 2079262dfd7fSJulian Elischer return; 2080262dfd7fSJulian Elischer } 2081262dfd7fSJulian Elischer 2082262dfd7fSJulian Elischer #endif 2083262dfd7fSJulian Elischer 20848f9ac44aSAlexander Motin /* Release reader lock. */ 2085069154d5SJulian Elischer static __inline void 2086069154d5SJulian Elischer ng_leave_read(struct ng_queue *ngq) 2087069154d5SJulian Elischer { 20888f9ac44aSAlexander Motin atomic_subtract_rel_long(&ngq->q_flags, READER_INCREMENT); 2089069154d5SJulian Elischer } 2090069154d5SJulian Elischer 20918f9ac44aSAlexander Motin /* Release writer lock. */ 2092069154d5SJulian Elischer static __inline void 2093069154d5SJulian Elischer ng_leave_write(struct ng_queue *ngq) 2094069154d5SJulian Elischer { 20958f9ac44aSAlexander Motin atomic_clear_rel_long(&ngq->q_flags, WRITER_ACTIVE); 2096069154d5SJulian Elischer } 2097069154d5SJulian Elischer 20988f9ac44aSAlexander Motin /* Purge node queue. Called on node shutdown. */ 2099069154d5SJulian Elischer static void 2100069154d5SJulian Elischer ng_flush_input_queue(struct ng_queue * ngq) 2101069154d5SJulian Elischer { 2102069154d5SJulian Elischer item_p item; 2103069154d5SJulian Elischer 21042c8dda8dSWojciech A. Koszek NG_QUEUE_LOCK(ngq); 21054be59335SGleb Smirnoff while (ngq->queue) { 2106069154d5SJulian Elischer item = ngq->queue; 2107069154d5SJulian Elischer ngq->queue = item->el_next; 2108069154d5SJulian Elischer if (ngq->last == &(item->el_next)) { 2109069154d5SJulian Elischer ngq->last = &(ngq->queue); 2110394cb30aSAlexander Motin atomic_clear_long(&ngq->q_flags, OP_PENDING); 2111069154d5SJulian Elischer } 21122c8dda8dSWojciech A. Koszek NG_QUEUE_UNLOCK(ngq); 21134be59335SGleb Smirnoff 21144be59335SGleb Smirnoff /* If the item is supplying a callback, call it with an error */ 211510e87318SAlexander Motin if (item->apply != NULL) { 211610e87318SAlexander Motin if (item->depth == 1) 211710e87318SAlexander Motin item->apply->error = ENOENT; 211810e87318SAlexander Motin if (refcount_release(&item->apply->refs)) { 211910e87318SAlexander Motin (*item->apply->apply)(item->apply->context, 212010e87318SAlexander Motin item->apply->error); 212110e87318SAlexander Motin } 2122eb2405ddSGleb Smirnoff } 2123505fad52SJulian Elischer NG_FREE_ITEM(item); 21242c8dda8dSWojciech A. Koszek NG_QUEUE_LOCK(ngq); 2125069154d5SJulian Elischer } 21262c8dda8dSWojciech A. Koszek NG_QUEUE_UNLOCK(ngq); 2127069154d5SJulian Elischer } 2128069154d5SJulian Elischer 2129069154d5SJulian Elischer /*********************************************************************** 2130069154d5SJulian Elischer * Externally visible method for sending or queueing messages or data. 2131069154d5SJulian Elischer ***********************************************************************/ 2132069154d5SJulian Elischer 2133069154d5SJulian Elischer /* 21341acb27c6SJulian Elischer * The module code should have filled out the item correctly by this stage: 2135069154d5SJulian Elischer * Common: 2136069154d5SJulian Elischer * reference to destination node. 2137069154d5SJulian Elischer * Reference to destination rcv hook if relevant. 2138e088dd4cSAlexander Motin * apply pointer must be or NULL or reference valid struct ng_apply_info. 2139069154d5SJulian Elischer * Data: 2140069154d5SJulian Elischer * pointer to mbuf 2141069154d5SJulian Elischer * Control_Message: 2142069154d5SJulian Elischer * pointer to msg. 2143069154d5SJulian Elischer * ID of original sender node. (return address) 21441acb27c6SJulian Elischer * Function: 21451acb27c6SJulian Elischer * Function pointer 21461acb27c6SJulian Elischer * void * argument 21471acb27c6SJulian Elischer * integer argument 2148069154d5SJulian Elischer * 2149069154d5SJulian Elischer * The nodes have several routines and macros to help with this task: 2150069154d5SJulian Elischer */ 2151069154d5SJulian Elischer 2152069154d5SJulian Elischer int 215342282202SGleb Smirnoff ng_snd_item(item_p item, int flags) 2154069154d5SJulian Elischer { 2155e088dd4cSAlexander Motin hook_p hook; 2156e088dd4cSAlexander Motin node_p node; 215742282202SGleb Smirnoff int queue, rw; 2158e088dd4cSAlexander Motin struct ng_queue *ngq; 215932b33288SGleb Smirnoff int error = 0; 2160069154d5SJulian Elischer 2161f50597f5SAlexander Motin /* We are sending item, so it must be present! */ 2162f50597f5SAlexander Motin KASSERT(item != NULL, ("ng_snd_item: item is NULL")); 2163e088dd4cSAlexander Motin 2164e088dd4cSAlexander Motin #ifdef NETGRAPH_DEBUG 2165e088dd4cSAlexander Motin _ngi_check(item, __FILE__, __LINE__); 2166e088dd4cSAlexander Motin #endif 2167e088dd4cSAlexander Motin 2168f50597f5SAlexander Motin /* Item was sent once more, postpone apply() call. */ 2169e088dd4cSAlexander Motin if (item->apply) 2170e088dd4cSAlexander Motin refcount_acquire(&item->apply->refs); 2171e088dd4cSAlexander Motin 2172e088dd4cSAlexander Motin node = NGI_NODE(item); 2173f50597f5SAlexander Motin /* Node is never optional. */ 2174f50597f5SAlexander Motin KASSERT(node != NULL, ("ng_snd_item: node is NULL")); 2175e088dd4cSAlexander Motin 2176e72a98f4SAlexander Motin hook = NGI_HOOK(item); 2177f50597f5SAlexander Motin /* Valid hook and mbuf are mandatory for data. */ 2178f50597f5SAlexander Motin if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) { 2179f50597f5SAlexander Motin KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL")); 218010204449SJulian Elischer if (NGI_M(item) == NULL) 2181e088dd4cSAlexander Motin ERROUT(EINVAL); 2182069154d5SJulian Elischer CHECK_DATA_MBUF(NGI_M(item)); 21836f683eeeSGleb Smirnoff } 21846f683eeeSGleb Smirnoff 2185069154d5SJulian Elischer /* 2186f50597f5SAlexander Motin * If the item or the node specifies single threading, force 2187f50597f5SAlexander Motin * writer semantics. Similarly, the node may say one hook always 2188f50597f5SAlexander Motin * produces writers. These are overrides. 2189069154d5SJulian Elischer */ 2190db3408aeSAlexander Motin if (((item->el_flags & NGQF_RW) == NGQF_WRITER) || 2191f50597f5SAlexander Motin (node->nd_flags & NGF_FORCE_WRITER) || 2192f50597f5SAlexander Motin (hook && (hook->hk_flags & HK_FORCE_WRITER))) { 2193069154d5SJulian Elischer rw = NGQRW_W; 2194f50597f5SAlexander Motin } else { 2195f50597f5SAlexander Motin rw = NGQRW_R; 2196f50597f5SAlexander Motin } 21976f683eeeSGleb Smirnoff 219881a253a4SAlexander Motin /* 219981a253a4SAlexander Motin * If sender or receiver requests queued delivery or stack usage 220081a253a4SAlexander Motin * level is dangerous - enqueue message. 220181a253a4SAlexander Motin */ 220281a253a4SAlexander Motin if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) { 220381a253a4SAlexander Motin queue = 1; 2204f50597f5SAlexander Motin } else { 2205f50597f5SAlexander Motin queue = 0; 220681a253a4SAlexander Motin #ifdef GET_STACK_USAGE 2207d4529f98SAlexander Motin /* 2208942fe01fSDmitry Morozovsky * Most of netgraph nodes have small stack consumption and 2209f50597f5SAlexander Motin * for them 25% of free stack space is more than enough. 2210d4529f98SAlexander Motin * Nodes/hooks with higher stack usage should be marked as 2211f9773372SDmitry Morozovsky * HI_STACK. For them 50% of stack will be guaranteed then. 2212f50597f5SAlexander Motin * XXX: Values 25% and 50% are completely empirical. 2213d4529f98SAlexander Motin */ 2214f50597f5SAlexander Motin size_t st, su, sl; 221581a253a4SAlexander Motin GET_STACK_USAGE(st, su); 2216f50597f5SAlexander Motin sl = st - su; 2217f50597f5SAlexander Motin if ((sl * 4 < st) || 2218f50597f5SAlexander Motin ((sl * 2 < st) && ((node->nd_flags & NGF_HI_STACK) || 221981a253a4SAlexander Motin (hook && (hook->hk_flags & HK_HI_STACK))))) { 222081a253a4SAlexander Motin queue = 1; 222181a253a4SAlexander Motin } 222281a253a4SAlexander Motin #endif 2223f50597f5SAlexander Motin } 222481a253a4SAlexander Motin 2225e72a98f4SAlexander Motin ngq = &node->nd_input_queue; 2226069154d5SJulian Elischer if (queue) { 222710e87318SAlexander Motin item->depth = 1; 2228394cb30aSAlexander Motin /* Put it on the queue for that node*/ 2229069154d5SJulian Elischer ng_queue_rw(ngq, item, rw); 2230f50597f5SAlexander Motin return ((flags & NG_PROGRESS) ? EINPROGRESS : 0); 2231069154d5SJulian Elischer } 2232069154d5SJulian Elischer 2233069154d5SJulian Elischer /* 2234069154d5SJulian Elischer * We already decided how we will be queueud or treated. 2235069154d5SJulian Elischer * Try get the appropriate operating permission. 2236069154d5SJulian Elischer */ 223732b33288SGleb Smirnoff if (rw == NGQRW_R) 2238069154d5SJulian Elischer item = ng_acquire_read(ngq, item); 223932b33288SGleb Smirnoff else 2240069154d5SJulian Elischer item = ng_acquire_write(ngq, item); 22414cf49a43SJulian Elischer 2242f50597f5SAlexander Motin /* Item was queued while trying to get permission. */ 2243f50597f5SAlexander Motin if (item == NULL) 2244f50597f5SAlexander Motin return ((flags & NG_PROGRESS) ? EINPROGRESS : 0); 2245069154d5SJulian Elischer 22465951069aSJulian Elischer NGI_GET_NODE(item, node); /* zaps stored node */ 22475951069aSJulian Elischer 224810e87318SAlexander Motin item->depth++; 224927757487SGleb Smirnoff error = ng_apply_item(node, item, rw); /* drops r/w lock when done */ 2250069154d5SJulian Elischer 2251394cb30aSAlexander Motin /* If something is waiting on queue and ready, schedule it. */ 2252394cb30aSAlexander Motin if (QUEUE_ACTIVE(ngq)) { 22532c8dda8dSWojciech A. Koszek NG_QUEUE_LOCK(ngq); 2254394cb30aSAlexander Motin if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 2255394cb30aSAlexander Motin ng_worklist_add(ngq->q_node); 22562c8dda8dSWojciech A. Koszek NG_QUEUE_UNLOCK(ngq); 2257394cb30aSAlexander Motin } 2258394cb30aSAlexander Motin 2259394cb30aSAlexander Motin /* 2260394cb30aSAlexander Motin * Node may go away as soon as we remove the reference. 2261394cb30aSAlexander Motin * Whatever we do, DO NOT access the node again! 2262394cb30aSAlexander Motin */ 2263394cb30aSAlexander Motin NG_NODE_UNREF(node); 2264069154d5SJulian Elischer 22651acb27c6SJulian Elischer return (error); 2266e088dd4cSAlexander Motin 2267e088dd4cSAlexander Motin done: 2268f50597f5SAlexander Motin /* If was not sent, apply callback here. */ 226910e87318SAlexander Motin if (item->apply != NULL) { 227010e87318SAlexander Motin if (item->depth == 0 && error != 0) 227110e87318SAlexander Motin item->apply->error = error; 227210e87318SAlexander Motin if (refcount_release(&item->apply->refs)) { 227310e87318SAlexander Motin (*item->apply->apply)(item->apply->context, 227410e87318SAlexander Motin item->apply->error); 227510e87318SAlexander Motin } 227610e87318SAlexander Motin } 2277e72a98f4SAlexander Motin 2278e088dd4cSAlexander Motin NG_FREE_ITEM(item); 2279e088dd4cSAlexander Motin return (error); 2280069154d5SJulian Elischer } 2281069154d5SJulian Elischer 2282069154d5SJulian Elischer /* 2283069154d5SJulian Elischer * We have an item that was possibly queued somewhere. 2284069154d5SJulian Elischer * It should contain all the information needed 2285069154d5SJulian Elischer * to run it on the appropriate node/hook. 2286e088dd4cSAlexander Motin * If there is apply pointer and we own the last reference, call apply(). 22874cf49a43SJulian Elischer */ 228827757487SGleb Smirnoff static int 2289714fb865SGleb Smirnoff ng_apply_item(node_p node, item_p item, int rw) 2290069154d5SJulian Elischer { 2291069154d5SJulian Elischer hook_p hook; 2292069154d5SJulian Elischer ng_rcvdata_t *rcvdata; 2293c4b5eea4SJulian Elischer ng_rcvmsg_t *rcvmsg; 2294e088dd4cSAlexander Motin struct ng_apply_info *apply; 229510e87318SAlexander Motin int error = 0, depth; 2296069154d5SJulian Elischer 2297f50597f5SAlexander Motin /* Node and item are never optional. */ 2298f50597f5SAlexander Motin KASSERT(node != NULL, ("ng_apply_item: node is NULL")); 2299f50597f5SAlexander Motin KASSERT(item != NULL, ("ng_apply_item: item is NULL")); 2300f50597f5SAlexander Motin 23011acb27c6SJulian Elischer NGI_GET_HOOK(item, hook); /* clears stored hook */ 230230400f03SJulian Elischer #ifdef NETGRAPH_DEBUG 2303069154d5SJulian Elischer _ngi_check(item, __FILE__, __LINE__); 2304069154d5SJulian Elischer #endif 23058afe16d5SGleb Smirnoff 23068afe16d5SGleb Smirnoff apply = item->apply; 230710e87318SAlexander Motin depth = item->depth; 23088afe16d5SGleb Smirnoff 23096b795970SJulian Elischer switch (item->el_flags & NGQF_TYPE) { 2310069154d5SJulian Elischer case NGQF_DATA: 2311069154d5SJulian Elischer /* 2312069154d5SJulian Elischer * Check things are still ok as when we were queued. 2313069154d5SJulian Elischer */ 2314f50597f5SAlexander Motin KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL")); 2315f50597f5SAlexander Motin if (NG_HOOK_NOT_VALID(hook) || 2316f50597f5SAlexander Motin NG_NODE_NOT_VALID(node)) { 2317a54a69d7SJulian Elischer error = EIO; 2318069154d5SJulian Elischer NG_FREE_ITEM(item); 2319c4b5eea4SJulian Elischer break; 2320069154d5SJulian Elischer } 2321c4b5eea4SJulian Elischer /* 2322c4b5eea4SJulian Elischer * If no receive method, just silently drop it. 2323c4b5eea4SJulian Elischer * Give preference to the hook over-ride method 2324c4b5eea4SJulian Elischer */ 2325c4b5eea4SJulian Elischer if ((!(rcvdata = hook->hk_rcvdata)) 2326c4b5eea4SJulian Elischer && (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) { 2327c4b5eea4SJulian Elischer error = 0; 2328c4b5eea4SJulian Elischer NG_FREE_ITEM(item); 2329c4b5eea4SJulian Elischer break; 2330c4b5eea4SJulian Elischer } 2331a54a69d7SJulian Elischer error = (*rcvdata)(hook, item); 2332069154d5SJulian Elischer break; 2333069154d5SJulian Elischer case NGQF_MESG: 2334e72a98f4SAlexander Motin if (hook && NG_HOOK_NOT_VALID(hook)) { 2335069154d5SJulian Elischer /* 2336e72a98f4SAlexander Motin * The hook has been zapped then we can't use it. 2337e72a98f4SAlexander Motin * Immediately drop its reference. 2338069154d5SJulian Elischer * The message may not need it. 2339069154d5SJulian Elischer */ 234030400f03SJulian Elischer NG_HOOK_UNREF(hook); 2341069154d5SJulian Elischer hook = NULL; 2342069154d5SJulian Elischer } 2343069154d5SJulian Elischer /* 2344069154d5SJulian Elischer * Similarly, if the node is a zombie there is 2345069154d5SJulian Elischer * nothing we can do with it, drop everything. 2346069154d5SJulian Elischer */ 234730400f03SJulian Elischer if (NG_NODE_NOT_VALID(node)) { 23486b795970SJulian Elischer TRAP_ERROR(); 2349a54a69d7SJulian Elischer error = EINVAL; 2350069154d5SJulian Elischer NG_FREE_ITEM(item); 2351e72a98f4SAlexander Motin break; 2352e72a98f4SAlexander Motin } 2353069154d5SJulian Elischer /* 2354069154d5SJulian Elischer * Call the appropriate message handler for the object. 2355069154d5SJulian Elischer * It is up to the message handler to free the message. 2356069154d5SJulian Elischer * If it's a generic message, handle it generically, 2357e72a98f4SAlexander Motin * otherwise call the type's message handler (if it exists). 2358069154d5SJulian Elischer * XXX (race). Remember that a queued message may 2359069154d5SJulian Elischer * reference a node or hook that has just been 2360069154d5SJulian Elischer * invalidated. It will exist as the queue code 2361069154d5SJulian Elischer * is holding a reference, but.. 2362069154d5SJulian Elischer */ 2363e72a98f4SAlexander Motin if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) && 2364e72a98f4SAlexander Motin ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) { 2365a54a69d7SJulian Elischer error = ng_generic_msg(node, item, hook); 2366c4b5eea4SJulian Elischer break; 2367c4b5eea4SJulian Elischer } 2368e72a98f4SAlexander Motin if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) && 2369e72a98f4SAlexander Motin (!(rcvmsg = node->nd_type->rcvmsg))) { 23706b795970SJulian Elischer TRAP_ERROR(); 2371a54a69d7SJulian Elischer error = 0; 2372069154d5SJulian Elischer NG_FREE_ITEM(item); 2373c4b5eea4SJulian Elischer break; 2374069154d5SJulian Elischer } 2375a54a69d7SJulian Elischer error = (*rcvmsg)(node, item, hook); 2376069154d5SJulian Elischer break; 23776b795970SJulian Elischer case NGQF_FN: 2378e088dd4cSAlexander Motin case NGQF_FN2: 2379e088dd4cSAlexander Motin /* 2380e088dd4cSAlexander Motin * We have to implicitly trust the hook, 2381e088dd4cSAlexander Motin * as some of these are used for system purposes 2382e088dd4cSAlexander Motin * where the hook is invalid. In the case of 2383e088dd4cSAlexander Motin * the shutdown message we allow it to hit 2384e088dd4cSAlexander Motin * even if the node is invalid. 2385e088dd4cSAlexander Motin */ 2386e088dd4cSAlexander Motin if ((NG_NODE_NOT_VALID(node)) 2387e088dd4cSAlexander Motin && (NGI_FN(item) != &ng_rmnode)) { 2388e088dd4cSAlexander Motin TRAP_ERROR(); 2389e088dd4cSAlexander Motin error = EINVAL; 2390e088dd4cSAlexander Motin NG_FREE_ITEM(item); 2391e088dd4cSAlexander Motin break; 2392e088dd4cSAlexander Motin } 2393b332b91fSGleb Smirnoff if ((item->el_flags & NGQF_TYPE) == NGQF_FN) { 2394b332b91fSGleb Smirnoff (*NGI_FN(item))(node, hook, NGI_ARG1(item), 2395b332b91fSGleb Smirnoff NGI_ARG2(item)); 2396b332b91fSGleb Smirnoff NG_FREE_ITEM(item); 2397b332b91fSGleb Smirnoff } else /* it is NGQF_FN2 */ 2398e088dd4cSAlexander Motin error = (*NGI_FN2(item))(node, item, hook); 2399e088dd4cSAlexander Motin break; 2400069154d5SJulian Elischer } 2401069154d5SJulian Elischer /* 2402069154d5SJulian Elischer * We held references on some of the resources 2403069154d5SJulian Elischer * that we took from the item. Now that we have 2404069154d5SJulian Elischer * finished doing everything, drop those references. 2405069154d5SJulian Elischer */ 2406e72a98f4SAlexander Motin if (hook) 240730400f03SJulian Elischer NG_HOOK_UNREF(hook); 2408069154d5SJulian Elischer 2409f50597f5SAlexander Motin if (rw == NGQRW_R) 241030400f03SJulian Elischer ng_leave_read(&node->nd_input_queue); 2411f50597f5SAlexander Motin else 241230400f03SJulian Elischer ng_leave_write(&node->nd_input_queue); 24138afe16d5SGleb Smirnoff 24148afe16d5SGleb Smirnoff /* Apply callback. */ 241510e87318SAlexander Motin if (apply != NULL) { 241610e87318SAlexander Motin if (depth == 1 && error != 0) 241710e87318SAlexander Motin apply->error = error; 241810e87318SAlexander Motin if (refcount_release(&apply->refs)) 241910e87318SAlexander Motin (*apply->apply)(apply->context, apply->error); 242010e87318SAlexander Motin } 24218afe16d5SGleb Smirnoff 242227757487SGleb Smirnoff return (error); 2423069154d5SJulian Elischer } 2424069154d5SJulian Elischer 2425069154d5SJulian Elischer /*********************************************************************** 2426069154d5SJulian Elischer * Implement the 'generic' control messages 2427069154d5SJulian Elischer ***********************************************************************/ 2428069154d5SJulian Elischer static int 2429069154d5SJulian Elischer ng_generic_msg(node_p here, item_p item, hook_p lasthook) 24304cf49a43SJulian Elischer { 24314cf49a43SJulian Elischer int error = 0; 2432069154d5SJulian Elischer struct ng_mesg *msg; 2433069154d5SJulian Elischer struct ng_mesg *resp = NULL; 24344cf49a43SJulian Elischer 2435069154d5SJulian Elischer NGI_GET_MSG(item, msg); 24364cf49a43SJulian Elischer if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 24376b795970SJulian Elischer TRAP_ERROR(); 2438069154d5SJulian Elischer error = EINVAL; 2439069154d5SJulian Elischer goto out; 24404cf49a43SJulian Elischer } 24414cf49a43SJulian Elischer switch (msg->header.cmd) { 24424cf49a43SJulian Elischer case NGM_SHUTDOWN: 24431acb27c6SJulian Elischer ng_rmnode(here, NULL, NULL, 0); 24444cf49a43SJulian Elischer break; 24454cf49a43SJulian Elischer case NGM_MKPEER: 24464cf49a43SJulian Elischer { 24474cf49a43SJulian Elischer struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 24484cf49a43SJulian Elischer 24494cf49a43SJulian Elischer if (msg->header.arglen != sizeof(*mkp)) { 24506b795970SJulian Elischer TRAP_ERROR(); 2451069154d5SJulian Elischer error = EINVAL; 2452069154d5SJulian Elischer break; 24534cf49a43SJulian Elischer } 24544cf49a43SJulian Elischer mkp->type[sizeof(mkp->type) - 1] = '\0'; 24554cf49a43SJulian Elischer mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 24564cf49a43SJulian Elischer mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 24574cf49a43SJulian Elischer error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 24584cf49a43SJulian Elischer break; 24594cf49a43SJulian Elischer } 24604cf49a43SJulian Elischer case NGM_CONNECT: 24614cf49a43SJulian Elischer { 24624cf49a43SJulian Elischer struct ngm_connect *const con = 24634cf49a43SJulian Elischer (struct ngm_connect *) msg->data; 24644cf49a43SJulian Elischer node_p node2; 24654cf49a43SJulian Elischer 24664cf49a43SJulian Elischer if (msg->header.arglen != sizeof(*con)) { 24676b795970SJulian Elischer TRAP_ERROR(); 2468069154d5SJulian Elischer error = EINVAL; 2469069154d5SJulian Elischer break; 24704cf49a43SJulian Elischer } 24714cf49a43SJulian Elischer con->path[sizeof(con->path) - 1] = '\0'; 24724cf49a43SJulian Elischer con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 24734cf49a43SJulian Elischer con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 2474069154d5SJulian Elischer /* Don't forget we get a reference.. */ 2475069154d5SJulian Elischer error = ng_path2noderef(here, con->path, &node2, NULL); 24764cf49a43SJulian Elischer if (error) 24774cf49a43SJulian Elischer break; 2478e088dd4cSAlexander Motin error = ng_con_nodes(item, here, con->ourhook, 2479e088dd4cSAlexander Motin node2, con->peerhook); 248030400f03SJulian Elischer NG_NODE_UNREF(node2); 24814cf49a43SJulian Elischer break; 24824cf49a43SJulian Elischer } 24834cf49a43SJulian Elischer case NGM_NAME: 24844cf49a43SJulian Elischer { 24854cf49a43SJulian Elischer struct ngm_name *const nam = (struct ngm_name *) msg->data; 24864cf49a43SJulian Elischer 24874cf49a43SJulian Elischer if (msg->header.arglen != sizeof(*nam)) { 24886b795970SJulian Elischer TRAP_ERROR(); 2489069154d5SJulian Elischer error = EINVAL; 2490069154d5SJulian Elischer break; 24914cf49a43SJulian Elischer } 24924cf49a43SJulian Elischer nam->name[sizeof(nam->name) - 1] = '\0'; 24934cf49a43SJulian Elischer error = ng_name_node(here, nam->name); 24944cf49a43SJulian Elischer break; 24954cf49a43SJulian Elischer } 24964cf49a43SJulian Elischer case NGM_RMHOOK: 24974cf49a43SJulian Elischer { 24984cf49a43SJulian Elischer struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 24994cf49a43SJulian Elischer hook_p hook; 25004cf49a43SJulian Elischer 25014cf49a43SJulian Elischer if (msg->header.arglen != sizeof(*rmh)) { 25026b795970SJulian Elischer TRAP_ERROR(); 2503069154d5SJulian Elischer error = EINVAL; 2504069154d5SJulian Elischer break; 25054cf49a43SJulian Elischer } 25064cf49a43SJulian Elischer rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 2507899e9c4eSArchie Cobbs if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 25084cf49a43SJulian Elischer ng_destroy_hook(hook); 25094cf49a43SJulian Elischer break; 25104cf49a43SJulian Elischer } 25114cf49a43SJulian Elischer case NGM_NODEINFO: 25124cf49a43SJulian Elischer { 25134cf49a43SJulian Elischer struct nodeinfo *ni; 25144cf49a43SJulian Elischer 2515069154d5SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT); 25164cf49a43SJulian Elischer if (resp == NULL) { 25174cf49a43SJulian Elischer error = ENOMEM; 25184cf49a43SJulian Elischer break; 25194cf49a43SJulian Elischer } 25204cf49a43SJulian Elischer 25214cf49a43SJulian Elischer /* Fill in node info */ 2522069154d5SJulian Elischer ni = (struct nodeinfo *) resp->data; 252330400f03SJulian Elischer if (NG_NODE_HAS_NAME(here)) 252487e2c66aSHartmut Brandt strcpy(ni->name, NG_NODE_NAME(here)); 252587e2c66aSHartmut Brandt strcpy(ni->type, here->nd_type->name); 2526dc90cad9SJulian Elischer ni->id = ng_node2ID(here); 252730400f03SJulian Elischer ni->hooks = here->nd_numhooks; 25284cf49a43SJulian Elischer break; 25294cf49a43SJulian Elischer } 25304cf49a43SJulian Elischer case NGM_LISTHOOKS: 25314cf49a43SJulian Elischer { 253230400f03SJulian Elischer const int nhooks = here->nd_numhooks; 25334cf49a43SJulian Elischer struct hooklist *hl; 25344cf49a43SJulian Elischer struct nodeinfo *ni; 25354cf49a43SJulian Elischer hook_p hook; 25364cf49a43SJulian Elischer 25374cf49a43SJulian Elischer /* Get response struct */ 2538069154d5SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(*hl) 25394cf49a43SJulian Elischer + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 2540069154d5SJulian Elischer if (resp == NULL) { 25414cf49a43SJulian Elischer error = ENOMEM; 25424cf49a43SJulian Elischer break; 25434cf49a43SJulian Elischer } 2544069154d5SJulian Elischer hl = (struct hooklist *) resp->data; 25454cf49a43SJulian Elischer ni = &hl->nodeinfo; 25464cf49a43SJulian Elischer 25474cf49a43SJulian Elischer /* Fill in node info */ 254830400f03SJulian Elischer if (NG_NODE_HAS_NAME(here)) 254987e2c66aSHartmut Brandt strcpy(ni->name, NG_NODE_NAME(here)); 255087e2c66aSHartmut Brandt strcpy(ni->type, here->nd_type->name); 2551dc90cad9SJulian Elischer ni->id = ng_node2ID(here); 25524cf49a43SJulian Elischer 25534cf49a43SJulian Elischer /* Cycle through the linked list of hooks */ 25544cf49a43SJulian Elischer ni->hooks = 0; 255530400f03SJulian Elischer LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) { 25564cf49a43SJulian Elischer struct linkinfo *const link = &hl->link[ni->hooks]; 25574cf49a43SJulian Elischer 25584cf49a43SJulian Elischer if (ni->hooks >= nhooks) { 25594cf49a43SJulian Elischer log(LOG_ERR, "%s: number of %s changed\n", 25606e551fb6SDavid E. O'Brien __func__, "hooks"); 25614cf49a43SJulian Elischer break; 25624cf49a43SJulian Elischer } 256330400f03SJulian Elischer if (NG_HOOK_NOT_VALID(hook)) 25644cf49a43SJulian Elischer continue; 256587e2c66aSHartmut Brandt strcpy(link->ourhook, NG_HOOK_NAME(hook)); 256687e2c66aSHartmut Brandt strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook)); 256730400f03SJulian Elischer if (NG_PEER_NODE_NAME(hook)[0] != '\0') 256887e2c66aSHartmut Brandt strcpy(link->nodeinfo.name, 256987e2c66aSHartmut Brandt NG_PEER_NODE_NAME(hook)); 257087e2c66aSHartmut Brandt strcpy(link->nodeinfo.type, 257187e2c66aSHartmut Brandt NG_PEER_NODE(hook)->nd_type->name); 257230400f03SJulian Elischer link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook)); 257330400f03SJulian Elischer link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks; 25744cf49a43SJulian Elischer ni->hooks++; 25754cf49a43SJulian Elischer } 25764cf49a43SJulian Elischer break; 25774cf49a43SJulian Elischer } 25784cf49a43SJulian Elischer 25794cf49a43SJulian Elischer case NGM_LISTNAMES: 25804cf49a43SJulian Elischer case NGM_LISTNODES: 25814cf49a43SJulian Elischer { 25824cf49a43SJulian Elischer const int unnamed = (msg->header.cmd == NGM_LISTNODES); 25834cf49a43SJulian Elischer struct namelist *nl; 25844cf49a43SJulian Elischer node_p node; 2585cfea3f85SAlexander Motin int num = 0, i; 25864cf49a43SJulian Elischer 2587cfea3f85SAlexander Motin mtx_lock(&ng_namehash_mtx); 25884cf49a43SJulian Elischer /* Count number of nodes */ 2589cfea3f85SAlexander Motin for (i = 0; i < NG_NAME_HASH_SIZE; i++) { 2590cfea3f85SAlexander Motin LIST_FOREACH(node, &ng_name_hash[i], nd_nodes) { 2591cfea3f85SAlexander Motin if (NG_NODE_IS_VALID(node) && 2592cfea3f85SAlexander Motin (unnamed || NG_NODE_HAS_NAME(node))) { 25934cf49a43SJulian Elischer num++; 25944cf49a43SJulian Elischer } 259570de87f2SJulian Elischer } 2596cfea3f85SAlexander Motin } 2597cfea3f85SAlexander Motin mtx_unlock(&ng_namehash_mtx); 25984cf49a43SJulian Elischer 25994cf49a43SJulian Elischer /* Get response struct */ 2600069154d5SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(*nl) 26014cf49a43SJulian Elischer + (num * sizeof(struct nodeinfo)), M_NOWAIT); 2602069154d5SJulian Elischer if (resp == NULL) { 26034cf49a43SJulian Elischer error = ENOMEM; 26044cf49a43SJulian Elischer break; 26054cf49a43SJulian Elischer } 2606069154d5SJulian Elischer nl = (struct namelist *) resp->data; 26074cf49a43SJulian Elischer 26084cf49a43SJulian Elischer /* Cycle through the linked list of nodes */ 26094cf49a43SJulian Elischer nl->numnames = 0; 2610cfea3f85SAlexander Motin mtx_lock(&ng_namehash_mtx); 2611cfea3f85SAlexander Motin for (i = 0; i < NG_NAME_HASH_SIZE; i++) { 2612cfea3f85SAlexander Motin LIST_FOREACH(node, &ng_name_hash[i], nd_nodes) { 2613cfea3f85SAlexander Motin struct nodeinfo *const np = 2614cfea3f85SAlexander Motin &nl->nodeinfo[nl->numnames]; 26154cf49a43SJulian Elischer 2616b96baf0aSGleb Smirnoff if (NG_NODE_NOT_VALID(node)) 2617b96baf0aSGleb Smirnoff continue; 2618b96baf0aSGleb Smirnoff if (!unnamed && (! NG_NODE_HAS_NAME(node))) 2619b96baf0aSGleb Smirnoff continue; 26204cf49a43SJulian Elischer if (nl->numnames >= num) { 2621cfea3f85SAlexander Motin log(LOG_ERR, "%s: number of nodes changed\n", 2622cfea3f85SAlexander Motin __func__); 26234cf49a43SJulian Elischer break; 26244cf49a43SJulian Elischer } 262530400f03SJulian Elischer if (NG_NODE_HAS_NAME(node)) 262687e2c66aSHartmut Brandt strcpy(np->name, NG_NODE_NAME(node)); 262787e2c66aSHartmut Brandt strcpy(np->type, node->nd_type->name); 2628dc90cad9SJulian Elischer np->id = ng_node2ID(node); 262930400f03SJulian Elischer np->hooks = node->nd_numhooks; 26304cf49a43SJulian Elischer nl->numnames++; 26314cf49a43SJulian Elischer } 2632cfea3f85SAlexander Motin } 2633cfea3f85SAlexander Motin mtx_unlock(&ng_namehash_mtx); 26344cf49a43SJulian Elischer break; 26354cf49a43SJulian Elischer } 26364cf49a43SJulian Elischer 26374cf49a43SJulian Elischer case NGM_LISTTYPES: 26384cf49a43SJulian Elischer { 26394cf49a43SJulian Elischer struct typelist *tl; 26404cf49a43SJulian Elischer struct ng_type *type; 26414cf49a43SJulian Elischer int num = 0; 26424cf49a43SJulian Elischer 26439ed346baSBosko Milekic mtx_lock(&ng_typelist_mtx); 26444cf49a43SJulian Elischer /* Count number of types */ 264570de87f2SJulian Elischer LIST_FOREACH(type, &ng_typelist, types) { 26464cf49a43SJulian Elischer num++; 264770de87f2SJulian Elischer } 26489ed346baSBosko Milekic mtx_unlock(&ng_typelist_mtx); 26494cf49a43SJulian Elischer 26504cf49a43SJulian Elischer /* Get response struct */ 2651069154d5SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(*tl) 26524cf49a43SJulian Elischer + (num * sizeof(struct typeinfo)), M_NOWAIT); 2653069154d5SJulian Elischer if (resp == NULL) { 26544cf49a43SJulian Elischer error = ENOMEM; 26554cf49a43SJulian Elischer break; 26564cf49a43SJulian Elischer } 2657069154d5SJulian Elischer tl = (struct typelist *) resp->data; 26584cf49a43SJulian Elischer 26594cf49a43SJulian Elischer /* Cycle through the linked list of types */ 26604cf49a43SJulian Elischer tl->numtypes = 0; 26619ed346baSBosko Milekic mtx_lock(&ng_typelist_mtx); 2662069154d5SJulian Elischer LIST_FOREACH(type, &ng_typelist, types) { 26634cf49a43SJulian Elischer struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 26644cf49a43SJulian Elischer 26654cf49a43SJulian Elischer if (tl->numtypes >= num) { 26664cf49a43SJulian Elischer log(LOG_ERR, "%s: number of %s changed\n", 26676e551fb6SDavid E. O'Brien __func__, "types"); 26684cf49a43SJulian Elischer break; 26694cf49a43SJulian Elischer } 267087e2c66aSHartmut Brandt strcpy(tp->type_name, type->name); 2671c73b94a2SJulian Elischer tp->numnodes = type->refs - 1; /* don't count list */ 26724cf49a43SJulian Elischer tl->numtypes++; 26734cf49a43SJulian Elischer } 26749ed346baSBosko Milekic mtx_unlock(&ng_typelist_mtx); 26754cf49a43SJulian Elischer break; 26764cf49a43SJulian Elischer } 26774cf49a43SJulian Elischer 2678f8307e12SArchie Cobbs case NGM_BINARY2ASCII: 2679f8307e12SArchie Cobbs { 26807133ac27SArchie Cobbs int bufSize = 20 * 1024; /* XXX hard coded constant */ 2681f8307e12SArchie Cobbs const struct ng_parse_type *argstype; 2682f8307e12SArchie Cobbs const struct ng_cmdlist *c; 2683069154d5SJulian Elischer struct ng_mesg *binary, *ascii; 2684f8307e12SArchie Cobbs 2685f8307e12SArchie Cobbs /* Data area must contain a valid netgraph message */ 2686f8307e12SArchie Cobbs binary = (struct ng_mesg *)msg->data; 26874c9b5910SGleb Smirnoff if (msg->header.arglen < sizeof(struct ng_mesg) || 26884c9b5910SGleb Smirnoff (msg->header.arglen - sizeof(struct ng_mesg) < 26894c9b5910SGleb Smirnoff binary->header.arglen)) { 26906b795970SJulian Elischer TRAP_ERROR(); 2691f8307e12SArchie Cobbs error = EINVAL; 2692f8307e12SArchie Cobbs break; 2693f8307e12SArchie Cobbs } 2694f8307e12SArchie Cobbs 2695f8307e12SArchie Cobbs /* Get a response message with lots of room */ 2696069154d5SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 2697069154d5SJulian Elischer if (resp == NULL) { 2698f8307e12SArchie Cobbs error = ENOMEM; 2699f8307e12SArchie Cobbs break; 2700f8307e12SArchie Cobbs } 2701069154d5SJulian Elischer ascii = (struct ng_mesg *)resp->data; 2702f8307e12SArchie Cobbs 2703f8307e12SArchie Cobbs /* Copy binary message header to response message payload */ 2704f8307e12SArchie Cobbs bcopy(binary, ascii, sizeof(*binary)); 2705f8307e12SArchie Cobbs 2706f8307e12SArchie Cobbs /* Find command by matching typecookie and command number */ 270730400f03SJulian Elischer for (c = here->nd_type->cmdlist; 2708f8307e12SArchie Cobbs c != NULL && c->name != NULL; c++) { 2709f8307e12SArchie Cobbs if (binary->header.typecookie == c->cookie 2710f8307e12SArchie Cobbs && binary->header.cmd == c->cmd) 2711f8307e12SArchie Cobbs break; 2712f8307e12SArchie Cobbs } 2713f8307e12SArchie Cobbs if (c == NULL || c->name == NULL) { 2714f8307e12SArchie Cobbs for (c = ng_generic_cmds; c->name != NULL; c++) { 2715f8307e12SArchie Cobbs if (binary->header.typecookie == c->cookie 2716f8307e12SArchie Cobbs && binary->header.cmd == c->cmd) 2717f8307e12SArchie Cobbs break; 2718f8307e12SArchie Cobbs } 2719f8307e12SArchie Cobbs if (c->name == NULL) { 2720069154d5SJulian Elischer NG_FREE_MSG(resp); 2721f8307e12SArchie Cobbs error = ENOSYS; 2722f8307e12SArchie Cobbs break; 2723f8307e12SArchie Cobbs } 2724f8307e12SArchie Cobbs } 2725f8307e12SArchie Cobbs 2726f8307e12SArchie Cobbs /* Convert command name to ASCII */ 2727f8307e12SArchie Cobbs snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 2728f8307e12SArchie Cobbs "%s", c->name); 2729f8307e12SArchie Cobbs 2730f8307e12SArchie Cobbs /* Convert command arguments to ASCII */ 2731f8307e12SArchie Cobbs argstype = (binary->header.flags & NGF_RESP) ? 2732f8307e12SArchie Cobbs c->respType : c->mesgType; 273370de87f2SJulian Elischer if (argstype == NULL) { 2734f8307e12SArchie Cobbs *ascii->data = '\0'; 273570de87f2SJulian Elischer } else { 2736f8307e12SArchie Cobbs if ((error = ng_unparse(argstype, 2737f8307e12SArchie Cobbs (u_char *)binary->data, 2738f8307e12SArchie Cobbs ascii->data, bufSize)) != 0) { 2739069154d5SJulian Elischer NG_FREE_MSG(resp); 2740f8307e12SArchie Cobbs break; 2741f8307e12SArchie Cobbs } 2742f8307e12SArchie Cobbs } 2743f8307e12SArchie Cobbs 2744f8307e12SArchie Cobbs /* Return the result as struct ng_mesg plus ASCII string */ 2745f8307e12SArchie Cobbs bufSize = strlen(ascii->data) + 1; 2746f8307e12SArchie Cobbs ascii->header.arglen = bufSize; 2747069154d5SJulian Elischer resp->header.arglen = sizeof(*ascii) + bufSize; 2748f8307e12SArchie Cobbs break; 2749f8307e12SArchie Cobbs } 2750f8307e12SArchie Cobbs 2751f8307e12SArchie Cobbs case NGM_ASCII2BINARY: 2752f8307e12SArchie Cobbs { 2753f8307e12SArchie Cobbs int bufSize = 2000; /* XXX hard coded constant */ 2754f8307e12SArchie Cobbs const struct ng_cmdlist *c; 2755f8307e12SArchie Cobbs const struct ng_parse_type *argstype; 2756069154d5SJulian Elischer struct ng_mesg *ascii, *binary; 275752ec4a03SArchie Cobbs int off = 0; 2758f8307e12SArchie Cobbs 2759f8307e12SArchie Cobbs /* Data area must contain at least a struct ng_mesg + '\0' */ 2760f8307e12SArchie Cobbs ascii = (struct ng_mesg *)msg->data; 27614c9b5910SGleb Smirnoff if ((msg->header.arglen < sizeof(*ascii) + 1) || 27624c9b5910SGleb Smirnoff (ascii->header.arglen < 1) || 27634c9b5910SGleb Smirnoff (msg->header.arglen < sizeof(*ascii) + 27644c9b5910SGleb Smirnoff ascii->header.arglen)) { 27656b795970SJulian Elischer TRAP_ERROR(); 2766f8307e12SArchie Cobbs error = EINVAL; 2767f8307e12SArchie Cobbs break; 2768f8307e12SArchie Cobbs } 2769f8307e12SArchie Cobbs ascii->data[ascii->header.arglen - 1] = '\0'; 2770f8307e12SArchie Cobbs 2771f8307e12SArchie Cobbs /* Get a response message with lots of room */ 2772069154d5SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 2773069154d5SJulian Elischer if (resp == NULL) { 2774f8307e12SArchie Cobbs error = ENOMEM; 2775f8307e12SArchie Cobbs break; 2776f8307e12SArchie Cobbs } 2777069154d5SJulian Elischer binary = (struct ng_mesg *)resp->data; 2778f8307e12SArchie Cobbs 2779f8307e12SArchie Cobbs /* Copy ASCII message header to response message payload */ 2780f8307e12SArchie Cobbs bcopy(ascii, binary, sizeof(*ascii)); 2781f8307e12SArchie Cobbs 2782f8307e12SArchie Cobbs /* Find command by matching ASCII command string */ 278330400f03SJulian Elischer for (c = here->nd_type->cmdlist; 2784f8307e12SArchie Cobbs c != NULL && c->name != NULL; c++) { 2785f8307e12SArchie Cobbs if (strcmp(ascii->header.cmdstr, c->name) == 0) 2786f8307e12SArchie Cobbs break; 2787f8307e12SArchie Cobbs } 2788f8307e12SArchie Cobbs if (c == NULL || c->name == NULL) { 2789f8307e12SArchie Cobbs for (c = ng_generic_cmds; c->name != NULL; c++) { 2790f8307e12SArchie Cobbs if (strcmp(ascii->header.cmdstr, c->name) == 0) 2791f8307e12SArchie Cobbs break; 2792f8307e12SArchie Cobbs } 2793f8307e12SArchie Cobbs if (c->name == NULL) { 2794069154d5SJulian Elischer NG_FREE_MSG(resp); 2795f8307e12SArchie Cobbs error = ENOSYS; 2796f8307e12SArchie Cobbs break; 2797f8307e12SArchie Cobbs } 2798f8307e12SArchie Cobbs } 2799f8307e12SArchie Cobbs 2800f8307e12SArchie Cobbs /* Convert command name to binary */ 2801f8307e12SArchie Cobbs binary->header.cmd = c->cmd; 2802f8307e12SArchie Cobbs binary->header.typecookie = c->cookie; 2803f8307e12SArchie Cobbs 2804f8307e12SArchie Cobbs /* Convert command arguments to binary */ 2805f8307e12SArchie Cobbs argstype = (binary->header.flags & NGF_RESP) ? 2806f8307e12SArchie Cobbs c->respType : c->mesgType; 280770de87f2SJulian Elischer if (argstype == NULL) { 2808f8307e12SArchie Cobbs bufSize = 0; 280970de87f2SJulian Elischer } else { 2810f8307e12SArchie Cobbs if ((error = ng_parse(argstype, ascii->data, 2811f8307e12SArchie Cobbs &off, (u_char *)binary->data, &bufSize)) != 0) { 2812069154d5SJulian Elischer NG_FREE_MSG(resp); 2813f8307e12SArchie Cobbs break; 2814f8307e12SArchie Cobbs } 2815f8307e12SArchie Cobbs } 2816f8307e12SArchie Cobbs 2817f8307e12SArchie Cobbs /* Return the result */ 2818f8307e12SArchie Cobbs binary->header.arglen = bufSize; 2819069154d5SJulian Elischer resp->header.arglen = sizeof(*binary) + bufSize; 2820f8307e12SArchie Cobbs break; 2821f8307e12SArchie Cobbs } 2822f8307e12SArchie Cobbs 28237095e097SPoul-Henning Kamp case NGM_TEXT_CONFIG: 28244cf49a43SJulian Elischer case NGM_TEXT_STATUS: 28254cf49a43SJulian Elischer /* 28264cf49a43SJulian Elischer * This one is tricky as it passes the command down to the 28274cf49a43SJulian Elischer * actual node, even though it is a generic type command. 2828069154d5SJulian Elischer * This means we must assume that the item/msg is already freed 28294cf49a43SJulian Elischer * when control passes back to us. 28304cf49a43SJulian Elischer */ 283130400f03SJulian Elischer if (here->nd_type->rcvmsg != NULL) { 2832069154d5SJulian Elischer NGI_MSG(item) = msg; /* put it back as we found it */ 283330400f03SJulian Elischer return((*here->nd_type->rcvmsg)(here, item, lasthook)); 28344cf49a43SJulian Elischer } 28354cf49a43SJulian Elischer /* Fall through if rcvmsg not supported */ 28364cf49a43SJulian Elischer default: 28376b795970SJulian Elischer TRAP_ERROR(); 28384cf49a43SJulian Elischer error = EINVAL; 28394cf49a43SJulian Elischer } 2840069154d5SJulian Elischer /* 2841069154d5SJulian Elischer * Sometimes a generic message may be statically allocated 2842069154d5SJulian Elischer * to avoid problems with allocating when in tight memeory situations. 2843069154d5SJulian Elischer * Don't free it if it is so. 2844069154d5SJulian Elischer * I break them appart here, because erros may cause a free if the item 2845069154d5SJulian Elischer * in which case we'd be doing it twice. 2846069154d5SJulian Elischer * they are kept together above, to simplify freeing. 2847069154d5SJulian Elischer */ 2848069154d5SJulian Elischer out: 2849069154d5SJulian Elischer NG_RESPOND_MSG(error, here, item, resp); 28501acb27c6SJulian Elischer if (msg) 2851069154d5SJulian Elischer NG_FREE_MSG(msg); 28524cf49a43SJulian Elischer return (error); 28534cf49a43SJulian Elischer } 28544cf49a43SJulian Elischer 28554cf49a43SJulian Elischer /************************************************************************ 28568253c060SGleb Smirnoff Queue element get/free routines 28578253c060SGleb Smirnoff ************************************************************************/ 28588253c060SGleb Smirnoff 28598253c060SGleb Smirnoff uma_zone_t ng_qzone; 2860ed75521fSAlexander Motin static int maxalloc = 4096;/* limit the damage of a leak */ 2861ed75521fSAlexander Motin static int maxdata = 512; /* limit the damage of a DoS */ 2862ed75521fSAlexander Motin static int useddata = 0; 28638253c060SGleb Smirnoff 28648253c060SGleb Smirnoff TUNABLE_INT("net.graph.maxalloc", &maxalloc); 28658253c060SGleb Smirnoff SYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc, 28668253c060SGleb Smirnoff 0, "Maximum number of queue items to allocate"); 2867ed75521fSAlexander Motin TUNABLE_INT("net.graph.maxdata", &maxdata); 2868ed75521fSAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RW | CTLFLAG_TUN, &maxdata, 2869ed75521fSAlexander Motin 0, "Maximum number of queue data items to allocate"); 28708253c060SGleb Smirnoff 28718253c060SGleb Smirnoff #ifdef NETGRAPH_DEBUG 28728253c060SGleb Smirnoff static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist); 28738253c060SGleb Smirnoff static int allocated; /* number of items malloc'd */ 28748253c060SGleb Smirnoff #endif 28758253c060SGleb Smirnoff 28768253c060SGleb Smirnoff /* 28778253c060SGleb Smirnoff * Get a queue entry. 28788253c060SGleb Smirnoff * This is usually called when a packet first enters netgraph. 28798253c060SGleb Smirnoff * By definition, this is usually from an interrupt, or from a user. 28808253c060SGleb Smirnoff * Users are not so important, but try be quick for the times that it's 28818253c060SGleb Smirnoff * an interrupt. 28828253c060SGleb Smirnoff */ 28838253c060SGleb Smirnoff static __inline item_p 288442282202SGleb Smirnoff ng_getqblk(int flags) 28858253c060SGleb Smirnoff { 28868253c060SGleb Smirnoff item_p item = NULL; 288742282202SGleb Smirnoff int wait; 28888253c060SGleb Smirnoff 288942282202SGleb Smirnoff wait = (flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT; 289042282202SGleb Smirnoff 289142282202SGleb Smirnoff item = uma_zalloc(ng_qzone, wait | M_ZERO); 28928253c060SGleb Smirnoff 28938253c060SGleb Smirnoff #ifdef NETGRAPH_DEBUG 28948253c060SGleb Smirnoff if (item) { 28958253c060SGleb Smirnoff mtx_lock(&ngq_mtx); 28968253c060SGleb Smirnoff TAILQ_INSERT_TAIL(&ng_itemlist, item, all); 28978253c060SGleb Smirnoff allocated++; 28988253c060SGleb Smirnoff mtx_unlock(&ngq_mtx); 28998253c060SGleb Smirnoff } 29008253c060SGleb Smirnoff #endif 29018253c060SGleb Smirnoff 29028253c060SGleb Smirnoff return (item); 29038253c060SGleb Smirnoff } 29048253c060SGleb Smirnoff 29058253c060SGleb Smirnoff /* 29068253c060SGleb Smirnoff * Release a queue entry 29078253c060SGleb Smirnoff */ 29088253c060SGleb Smirnoff void 29098253c060SGleb Smirnoff ng_free_item(item_p item) 29108253c060SGleb Smirnoff { 29118253c060SGleb Smirnoff /* 29128253c060SGleb Smirnoff * The item may hold resources on it's own. We need to free 29138253c060SGleb Smirnoff * these before we can free the item. What they are depends upon 29148253c060SGleb Smirnoff * what kind of item it is. it is important that nodes zero 29158253c060SGleb Smirnoff * out pointers to resources that they remove from the item 29168253c060SGleb Smirnoff * or we release them again here. 29178253c060SGleb Smirnoff */ 29188253c060SGleb Smirnoff switch (item->el_flags & NGQF_TYPE) { 29198253c060SGleb Smirnoff case NGQF_DATA: 2920ed75521fSAlexander Motin atomic_subtract_int(&useddata, 1); 29218253c060SGleb Smirnoff /* If we have an mbuf still attached.. */ 29228253c060SGleb Smirnoff NG_FREE_M(_NGI_M(item)); 29238253c060SGleb Smirnoff break; 29248253c060SGleb Smirnoff case NGQF_MESG: 29258253c060SGleb Smirnoff _NGI_RETADDR(item) = 0; 29268253c060SGleb Smirnoff NG_FREE_MSG(_NGI_MSG(item)); 29278253c060SGleb Smirnoff break; 29288253c060SGleb Smirnoff case NGQF_FN: 2929e088dd4cSAlexander Motin case NGQF_FN2: 29308253c060SGleb Smirnoff /* nothing to free really, */ 29318253c060SGleb Smirnoff _NGI_FN(item) = NULL; 29328253c060SGleb Smirnoff _NGI_ARG1(item) = NULL; 29338253c060SGleb Smirnoff _NGI_ARG2(item) = 0; 29348253c060SGleb Smirnoff break; 29358253c060SGleb Smirnoff } 29368253c060SGleb Smirnoff /* If we still have a node or hook referenced... */ 29378253c060SGleb Smirnoff _NGI_CLR_NODE(item); 29388253c060SGleb Smirnoff _NGI_CLR_HOOK(item); 29398253c060SGleb Smirnoff 29408253c060SGleb Smirnoff #ifdef NETGRAPH_DEBUG 29418253c060SGleb Smirnoff mtx_lock(&ngq_mtx); 29428253c060SGleb Smirnoff TAILQ_REMOVE(&ng_itemlist, item, all); 29438253c060SGleb Smirnoff allocated--; 29448253c060SGleb Smirnoff mtx_unlock(&ngq_mtx); 29458253c060SGleb Smirnoff #endif 29468253c060SGleb Smirnoff uma_zfree(ng_qzone, item); 29478253c060SGleb Smirnoff } 29488253c060SGleb Smirnoff 29498253c060SGleb Smirnoff /************************************************************************ 29504cf49a43SJulian Elischer Module routines 29514cf49a43SJulian Elischer ************************************************************************/ 29524cf49a43SJulian Elischer 29534cf49a43SJulian Elischer /* 29544cf49a43SJulian Elischer * Handle the loading/unloading of a netgraph node type module 29554cf49a43SJulian Elischer */ 29564cf49a43SJulian Elischer int 29574cf49a43SJulian Elischer ng_mod_event(module_t mod, int event, void *data) 29584cf49a43SJulian Elischer { 29594cf49a43SJulian Elischer struct ng_type *const type = data; 29604cf49a43SJulian Elischer int s, error = 0; 29614cf49a43SJulian Elischer 29624cf49a43SJulian Elischer switch (event) { 29634cf49a43SJulian Elischer case MOD_LOAD: 29644cf49a43SJulian Elischer 29654cf49a43SJulian Elischer /* Register new netgraph node type */ 29664cf49a43SJulian Elischer s = splnet(); 29674cf49a43SJulian Elischer if ((error = ng_newtype(type)) != 0) { 29684cf49a43SJulian Elischer splx(s); 29694cf49a43SJulian Elischer break; 29704cf49a43SJulian Elischer } 29714cf49a43SJulian Elischer 29724cf49a43SJulian Elischer /* Call type specific code */ 29734cf49a43SJulian Elischer if (type->mod_event != NULL) 2974069154d5SJulian Elischer if ((error = (*type->mod_event)(mod, event, data))) { 29759ed346baSBosko Milekic mtx_lock(&ng_typelist_mtx); 2976c73b94a2SJulian Elischer type->refs--; /* undo it */ 29774cf49a43SJulian Elischer LIST_REMOVE(type, types); 29789ed346baSBosko Milekic mtx_unlock(&ng_typelist_mtx); 2979069154d5SJulian Elischer } 29804cf49a43SJulian Elischer splx(s); 29814cf49a43SJulian Elischer break; 29824cf49a43SJulian Elischer 29834cf49a43SJulian Elischer case MOD_UNLOAD: 29844cf49a43SJulian Elischer s = splnet(); 2985c73b94a2SJulian Elischer if (type->refs > 1) { /* make sure no nodes exist! */ 29864cf49a43SJulian Elischer error = EBUSY; 2987c73b94a2SJulian Elischer } else { 2988c73b94a2SJulian Elischer if (type->refs == 0) { 2989c73b94a2SJulian Elischer /* failed load, nothing to undo */ 2990c73b94a2SJulian Elischer splx(s); 2991c73b94a2SJulian Elischer break; 2992c73b94a2SJulian Elischer } 29934cf49a43SJulian Elischer if (type->mod_event != NULL) { /* check with type */ 29944cf49a43SJulian Elischer error = (*type->mod_event)(mod, event, data); 29954cf49a43SJulian Elischer if (error != 0) { /* type refuses.. */ 29964cf49a43SJulian Elischer splx(s); 29974cf49a43SJulian Elischer break; 29984cf49a43SJulian Elischer } 29994cf49a43SJulian Elischer } 30009ed346baSBosko Milekic mtx_lock(&ng_typelist_mtx); 30014cf49a43SJulian Elischer LIST_REMOVE(type, types); 30029ed346baSBosko Milekic mtx_unlock(&ng_typelist_mtx); 30034cf49a43SJulian Elischer } 30044cf49a43SJulian Elischer splx(s); 30054cf49a43SJulian Elischer break; 30064cf49a43SJulian Elischer 30074cf49a43SJulian Elischer default: 30084cf49a43SJulian Elischer if (type->mod_event != NULL) 30094cf49a43SJulian Elischer error = (*type->mod_event)(mod, event, data); 30104cf49a43SJulian Elischer else 30113e019deaSPoul-Henning Kamp error = EOPNOTSUPP; /* XXX ? */ 30124cf49a43SJulian Elischer break; 30134cf49a43SJulian Elischer } 30144cf49a43SJulian Elischer return (error); 30154cf49a43SJulian Elischer } 30164cf49a43SJulian Elischer 30174cf49a43SJulian Elischer /* 30184cf49a43SJulian Elischer * Handle loading and unloading for this code. 30194cf49a43SJulian Elischer * The only thing we need to link into is the NETISR strucure. 30204cf49a43SJulian Elischer */ 30214cf49a43SJulian Elischer static int 30224cf49a43SJulian Elischer ngb_mod_event(module_t mod, int event, void *data) 30234cf49a43SJulian Elischer { 30241489164fSGleb Smirnoff int error = 0; 30254cf49a43SJulian Elischer 30264cf49a43SJulian Elischer switch (event) { 30274cf49a43SJulian Elischer case MOD_LOAD: 30281489164fSGleb Smirnoff /* Initialize everything. */ 30292c8dda8dSWojciech A. Koszek NG_WORKLIST_LOCK_INIT(); 3030efd8e7c9SDon Lewis mtx_init(&ng_typelist_mtx, "netgraph types mutex", NULL, 3031efd8e7c9SDon Lewis MTX_DEF); 3032efd8e7c9SDon Lewis mtx_init(&ng_idhash_mtx, "netgraph idhash mutex", NULL, 3033efd8e7c9SDon Lewis MTX_DEF); 3034cfea3f85SAlexander Motin mtx_init(&ng_namehash_mtx, "netgraph namehash mutex", NULL, 3035cfea3f85SAlexander Motin MTX_DEF); 3036ac5dd141SGleb Smirnoff mtx_init(&ng_topo_mtx, "netgraph topology mutex", NULL, 3037ac5dd141SGleb Smirnoff MTX_DEF); 30381489164fSGleb Smirnoff #ifdef NETGRAPH_DEBUG 3039cfea3f85SAlexander Motin mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL, 3040cfea3f85SAlexander Motin MTX_DEF); 30411489164fSGleb Smirnoff mtx_init(&ngq_mtx, "netgraph item list mutex", NULL, 3042efd8e7c9SDon Lewis MTX_DEF); 30431489164fSGleb Smirnoff #endif 30441489164fSGleb Smirnoff ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item), 30451489164fSGleb Smirnoff NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0); 30461489164fSGleb Smirnoff uma_zone_set_max(ng_qzone, maxalloc); 304768780975SGleb Smirnoff netisr_register(NETISR_NETGRAPH, (netisr_t *)ngintr, NULL, 304868780975SGleb Smirnoff NETISR_MPSAFE); 30494cf49a43SJulian Elischer break; 30504cf49a43SJulian Elischer case MOD_UNLOAD: 305164efc707SRobert Watson /* You can't unload it because an interface may be using it. */ 30524cf49a43SJulian Elischer error = EBUSY; 30534cf49a43SJulian Elischer break; 30544cf49a43SJulian Elischer default: 30554cf49a43SJulian Elischer error = EOPNOTSUPP; 30564cf49a43SJulian Elischer break; 30574cf49a43SJulian Elischer } 30584cf49a43SJulian Elischer return (error); 30594cf49a43SJulian Elischer } 30604cf49a43SJulian Elischer 30614cf49a43SJulian Elischer static moduledata_t netgraph_mod = { 30624cf49a43SJulian Elischer "netgraph", 30634cf49a43SJulian Elischer ngb_mod_event, 30644cf49a43SJulian Elischer (NULL) 30654cf49a43SJulian Elischer }; 3066aa38f8f9SMaksim Yevmenkin DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_MIDDLE); 3067bfa7e882SJulian Elischer SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family"); 3068bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,""); 3069bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, ""); 30704cf49a43SJulian Elischer 307130400f03SJulian Elischer #ifdef NETGRAPH_DEBUG 307230400f03SJulian Elischer void 307330400f03SJulian Elischer dumphook (hook_p hook, char *file, int line) 307430400f03SJulian Elischer { 307530400f03SJulian Elischer printf("hook: name %s, %d refs, Last touched:\n", 307630400f03SJulian Elischer _NG_HOOK_NAME(hook), hook->hk_refs); 307730400f03SJulian Elischer printf(" Last active @ %s, line %d\n", 307830400f03SJulian Elischer hook->lastfile, hook->lastline); 307930400f03SJulian Elischer if (line) { 308030400f03SJulian Elischer printf(" problem discovered at file %s, line %d\n", file, line); 308130400f03SJulian Elischer } 308230400f03SJulian Elischer } 308330400f03SJulian Elischer 308430400f03SJulian Elischer void 308530400f03SJulian Elischer dumpnode(node_p node, char *file, int line) 308630400f03SJulian Elischer { 308730400f03SJulian Elischer printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n", 30886b795970SJulian Elischer _NG_NODE_ID(node), node->nd_type->name, 308930400f03SJulian Elischer node->nd_numhooks, node->nd_flags, 309030400f03SJulian Elischer node->nd_refs, node->nd_name); 309130400f03SJulian Elischer printf(" Last active @ %s, line %d\n", 309230400f03SJulian Elischer node->lastfile, node->lastline); 309330400f03SJulian Elischer if (line) { 309430400f03SJulian Elischer printf(" problem discovered at file %s, line %d\n", file, line); 309530400f03SJulian Elischer } 309630400f03SJulian Elischer } 309730400f03SJulian Elischer 3098069154d5SJulian Elischer void 3099069154d5SJulian Elischer dumpitem(item_p item, char *file, int line) 3100069154d5SJulian Elischer { 3101069154d5SJulian Elischer printf(" ACTIVE item, last used at %s, line %d", 3102069154d5SJulian Elischer item->lastfile, item->lastline); 31036b795970SJulian Elischer switch(item->el_flags & NGQF_TYPE) { 31046b795970SJulian Elischer case NGQF_DATA: 3105069154d5SJulian Elischer printf(" - [data]\n"); 31066b795970SJulian Elischer break; 31076b795970SJulian Elischer case NGQF_MESG: 31086b795970SJulian Elischer printf(" - retaddr[%d]:\n", _NGI_RETADDR(item)); 31096b795970SJulian Elischer break; 31106b795970SJulian Elischer case NGQF_FN: 3111857304e6SRuslan Ermilov printf(" - fn@%p (%p, %p, %p, %d (%x))\n", 3112857304e6SRuslan Ermilov _NGI_FN(item), 3113857304e6SRuslan Ermilov _NGI_NODE(item), 3114857304e6SRuslan Ermilov _NGI_HOOK(item), 3115857304e6SRuslan Ermilov item->body.fn.fn_arg1, 3116857304e6SRuslan Ermilov item->body.fn.fn_arg2, 3117857304e6SRuslan Ermilov item->body.fn.fn_arg2); 3118857304e6SRuslan Ermilov break; 3119e088dd4cSAlexander Motin case NGQF_FN2: 3120eb4687d2SAlexander Motin printf(" - fn2@%p (%p, %p, %p, %d (%x))\n", 3121857304e6SRuslan Ermilov _NGI_FN2(item), 31226064e568SGleb Smirnoff _NGI_NODE(item), 31236064e568SGleb Smirnoff _NGI_HOOK(item), 31246b795970SJulian Elischer item->body.fn.fn_arg1, 31256b795970SJulian Elischer item->body.fn.fn_arg2, 31266b795970SJulian Elischer item->body.fn.fn_arg2); 31276b795970SJulian Elischer break; 3128069154d5SJulian Elischer } 312930400f03SJulian Elischer if (line) { 3130069154d5SJulian Elischer printf(" problem discovered at file %s, line %d\n", file, line); 31316064e568SGleb Smirnoff if (_NGI_NODE(item)) { 313230400f03SJulian Elischer printf("node %p ([%x])\n", 31336064e568SGleb Smirnoff _NGI_NODE(item), ng_node2ID(_NGI_NODE(item))); 3134069154d5SJulian Elischer } 313530400f03SJulian Elischer } 313630400f03SJulian Elischer } 313730400f03SJulian Elischer 313830400f03SJulian Elischer static void 313930400f03SJulian Elischer ng_dumpitems(void) 314030400f03SJulian Elischer { 314130400f03SJulian Elischer item_p item; 314230400f03SJulian Elischer int i = 1; 314330400f03SJulian Elischer TAILQ_FOREACH(item, &ng_itemlist, all) { 314430400f03SJulian Elischer printf("[%d] ", i++); 314530400f03SJulian Elischer dumpitem(item, NULL, 0); 314630400f03SJulian Elischer } 314730400f03SJulian Elischer } 314830400f03SJulian Elischer 314930400f03SJulian Elischer static void 315030400f03SJulian Elischer ng_dumpnodes(void) 315130400f03SJulian Elischer { 315230400f03SJulian Elischer node_p node; 315330400f03SJulian Elischer int i = 1; 315453f9c5e9SRobert Watson mtx_lock(&ng_nodelist_mtx); 315530400f03SJulian Elischer SLIST_FOREACH(node, &ng_allnodes, nd_all) { 315630400f03SJulian Elischer printf("[%d] ", i++); 315730400f03SJulian Elischer dumpnode(node, NULL, 0); 315830400f03SJulian Elischer } 315953f9c5e9SRobert Watson mtx_unlock(&ng_nodelist_mtx); 316030400f03SJulian Elischer } 316130400f03SJulian Elischer 316230400f03SJulian Elischer static void 316330400f03SJulian Elischer ng_dumphooks(void) 316430400f03SJulian Elischer { 316530400f03SJulian Elischer hook_p hook; 316630400f03SJulian Elischer int i = 1; 316753f9c5e9SRobert Watson mtx_lock(&ng_nodelist_mtx); 316830400f03SJulian Elischer SLIST_FOREACH(hook, &ng_allhooks, hk_all) { 316930400f03SJulian Elischer printf("[%d] ", i++); 317030400f03SJulian Elischer dumphook(hook, NULL, 0); 317130400f03SJulian Elischer } 317253f9c5e9SRobert Watson mtx_unlock(&ng_nodelist_mtx); 317330400f03SJulian Elischer } 3174069154d5SJulian Elischer 3175069154d5SJulian Elischer static int 3176069154d5SJulian Elischer sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS) 3177069154d5SJulian Elischer { 3178069154d5SJulian Elischer int error; 3179069154d5SJulian Elischer int val; 3180069154d5SJulian Elischer int i; 3181069154d5SJulian Elischer 3182069154d5SJulian Elischer val = allocated; 3183069154d5SJulian Elischer i = 1; 3184041b706bSDavid Malone error = sysctl_handle_int(oidp, &val, 0, req); 31856b795970SJulian Elischer if (error != 0 || req->newptr == NULL) 31866b795970SJulian Elischer return (error); 31876b795970SJulian Elischer if (val == 42) { 318830400f03SJulian Elischer ng_dumpitems(); 318930400f03SJulian Elischer ng_dumpnodes(); 319030400f03SJulian Elischer ng_dumphooks(); 3191069154d5SJulian Elischer } 31926b795970SJulian Elischer return (0); 3193069154d5SJulian Elischer } 3194069154d5SJulian Elischer 31956b795970SJulian Elischer SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW, 31966b795970SJulian Elischer 0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items"); 319730400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ 3198069154d5SJulian Elischer 3199069154d5SJulian Elischer 3200069154d5SJulian Elischer /*********************************************************************** 3201069154d5SJulian Elischer * Worklist routines 3202069154d5SJulian Elischer **********************************************************************/ 3203069154d5SJulian Elischer /* NETISR thread enters here */ 3204069154d5SJulian Elischer /* 3205069154d5SJulian Elischer * Pick a node off the list of nodes with work, 3206069154d5SJulian Elischer * try get an item to process off it. 3207069154d5SJulian Elischer * If there are no more, remove the node from the list. 3208069154d5SJulian Elischer */ 3209069154d5SJulian Elischer static void 3210069154d5SJulian Elischer ngintr(void) 3211069154d5SJulian Elischer { 3212069154d5SJulian Elischer for (;;) { 3213394cb30aSAlexander Motin node_p node; 3214394cb30aSAlexander Motin 3215394cb30aSAlexander Motin /* Get node from the worklist. */ 32162c8dda8dSWojciech A. Koszek NG_WORKLIST_LOCK(); 3217069154d5SJulian Elischer node = TAILQ_FIRST(&ng_worklist); 3218069154d5SJulian Elischer if (!node) { 32192c8dda8dSWojciech A. Koszek NG_WORKLIST_UNLOCK(); 3220069154d5SJulian Elischer break; 3221069154d5SJulian Elischer } 322230400f03SJulian Elischer TAILQ_REMOVE(&ng_worklist, node, nd_work); 32232c8dda8dSWojciech A. Koszek NG_WORKLIST_UNLOCK(); 32242955ee18SGleb Smirnoff CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist", 32252955ee18SGleb Smirnoff __func__, node->nd_ID, node); 3226069154d5SJulian Elischer /* 3227069154d5SJulian Elischer * We have the node. We also take over the reference 3228069154d5SJulian Elischer * that the list had on it. 3229069154d5SJulian Elischer * Now process as much as you can, until it won't 3230069154d5SJulian Elischer * let you have another item off the queue. 3231069154d5SJulian Elischer * All this time, keep the reference 3232069154d5SJulian Elischer * that lets us be sure that the node still exists. 3233069154d5SJulian Elischer * Let the reference go at the last minute. 3234069154d5SJulian Elischer */ 3235069154d5SJulian Elischer for (;;) { 3236394cb30aSAlexander Motin item_p item; 3237714fb865SGleb Smirnoff int rw; 3238714fb865SGleb Smirnoff 32392c8dda8dSWojciech A. Koszek NG_QUEUE_LOCK(&node->nd_input_queue); 3240714fb865SGleb Smirnoff item = ng_dequeue(&node->nd_input_queue, &rw); 3241069154d5SJulian Elischer if (item == NULL) { 3242394cb30aSAlexander Motin atomic_clear_int(&node->nd_flags, NGF_WORKQ); 32432c8dda8dSWojciech A. Koszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 3244069154d5SJulian Elischer break; /* go look for another node */ 3245069154d5SJulian Elischer } else { 32462c8dda8dSWojciech A. Koszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 32475951069aSJulian Elischer NGI_GET_NODE(item, node); /* zaps stored node */ 3248714fb865SGleb Smirnoff ng_apply_item(node, item, rw); 32495951069aSJulian Elischer NG_NODE_UNREF(node); 3250069154d5SJulian Elischer } 3251069154d5SJulian Elischer } 3252a96dcd84SJulian Elischer NG_NODE_UNREF(node); 3253069154d5SJulian Elischer } 3254069154d5SJulian Elischer } 3255069154d5SJulian Elischer 325633338e73SJulian Elischer /* 325733338e73SJulian Elischer * XXX 325833338e73SJulian Elischer * It's posible that a debugging NG_NODE_REF may need 325933338e73SJulian Elischer * to be outside the mutex zone 326033338e73SJulian Elischer */ 3261069154d5SJulian Elischer static void 3262394cb30aSAlexander Motin ng_worklist_add(node_p node) 3263069154d5SJulian Elischer { 3264f912c0f7SGleb Smirnoff 32655bc15201SGleb Smirnoff mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED); 3266f912c0f7SGleb Smirnoff 3267be4252b3SJulian Elischer if ((node->nd_flags & NGF_WORKQ) == 0) { 3268069154d5SJulian Elischer /* 3269069154d5SJulian Elischer * If we are not already on the work queue, 3270069154d5SJulian Elischer * then put us on. 3271069154d5SJulian Elischer */ 3272394cb30aSAlexander Motin atomic_set_int(&node->nd_flags, NGF_WORKQ); 3273394cb30aSAlexander Motin NG_NODE_REF(node); /* XXX fafe in mutex? */ 32742c8dda8dSWojciech A. Koszek NG_WORKLIST_LOCK(); 327530400f03SJulian Elischer TAILQ_INSERT_TAIL(&ng_worklist, node, nd_work); 32762c8dda8dSWojciech A. Koszek NG_WORKLIST_UNLOCK(); 3277394cb30aSAlexander Motin schednetisr(NETISR_NETGRAPH); 32782955ee18SGleb Smirnoff CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__, 32792955ee18SGleb Smirnoff node->nd_ID, node); 3280394cb30aSAlexander Motin } else { 32812955ee18SGleb Smirnoff CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist", 32822955ee18SGleb Smirnoff __func__, node->nd_ID, node); 3283394cb30aSAlexander Motin } 3284069154d5SJulian Elischer } 3285069154d5SJulian Elischer 3286069154d5SJulian Elischer 3287069154d5SJulian Elischer /*********************************************************************** 3288069154d5SJulian Elischer * Externally useable functions to set up a queue item ready for sending 3289069154d5SJulian Elischer ***********************************************************************/ 3290069154d5SJulian Elischer 329130400f03SJulian Elischer #ifdef NETGRAPH_DEBUG 329230400f03SJulian Elischer #define ITEM_DEBUG_CHECKS \ 32934cf49a43SJulian Elischer do { \ 32941acb27c6SJulian Elischer if (NGI_NODE(item) ) { \ 3295069154d5SJulian Elischer printf("item already has node"); \ 32963de213ccSRobert Watson kdb_enter(KDB_WHY_NETGRAPH, "has node"); \ 32971acb27c6SJulian Elischer NGI_CLR_NODE(item); \ 3298069154d5SJulian Elischer } \ 32991acb27c6SJulian Elischer if (NGI_HOOK(item) ) { \ 3300069154d5SJulian Elischer printf("item already has hook"); \ 33013de213ccSRobert Watson kdb_enter(KDB_WHY_NETGRAPH, "has hook"); \ 33021acb27c6SJulian Elischer NGI_CLR_HOOK(item); \ 3303069154d5SJulian Elischer } \ 3304069154d5SJulian Elischer } while (0) 3305069154d5SJulian Elischer #else 330630400f03SJulian Elischer #define ITEM_DEBUG_CHECKS 3307069154d5SJulian Elischer #endif 3308069154d5SJulian Elischer 3309069154d5SJulian Elischer /* 33108ed370fdSJulian Elischer * Put mbuf into the item. 3311069154d5SJulian Elischer * Hook and node references will be removed when the item is dequeued. 3312069154d5SJulian Elischer * (or equivalent) 3313069154d5SJulian Elischer * (XXX) Unsafe because no reference held by peer on remote node. 3314069154d5SJulian Elischer * remote node might go away in this timescale. 3315069154d5SJulian Elischer * We know the hooks can't go away because that would require getting 3316069154d5SJulian Elischer * a writer item on both nodes and we must have at least a reader 33174be59335SGleb Smirnoff * here to be able to do this. 3318069154d5SJulian Elischer * Note that the hook loaded is the REMOTE hook. 3319069154d5SJulian Elischer * 3320069154d5SJulian Elischer * This is possibly in the critical path for new data. 3321069154d5SJulian Elischer */ 3322069154d5SJulian Elischer item_p 332342282202SGleb Smirnoff ng_package_data(struct mbuf *m, int flags) 3324069154d5SJulian Elischer { 3325069154d5SJulian Elischer item_p item; 3326069154d5SJulian Elischer 3327ed75521fSAlexander Motin if (atomic_fetchadd_int(&useddata, 1) >= maxdata) { 3328ed75521fSAlexander Motin atomic_subtract_int(&useddata, 1); 3329ed75521fSAlexander Motin NG_FREE_M(m); 3330ed75521fSAlexander Motin return (NULL); 3331ed75521fSAlexander Motin } 333242282202SGleb Smirnoff if ((item = ng_getqblk(flags)) == NULL) { 3333069154d5SJulian Elischer NG_FREE_M(m); 3334069154d5SJulian Elischer return (NULL); 3335069154d5SJulian Elischer } 333630400f03SJulian Elischer ITEM_DEBUG_CHECKS; 33376f683eeeSGleb Smirnoff item->el_flags = NGQF_DATA | NGQF_READER; 3338069154d5SJulian Elischer NGI_M(item) = m; 3339069154d5SJulian Elischer return (item); 3340069154d5SJulian Elischer } 3341069154d5SJulian Elischer 3342069154d5SJulian Elischer /* 3343069154d5SJulian Elischer * Allocate a queue item and put items into it.. 3344069154d5SJulian Elischer * Evaluate the address as this will be needed to queue it and 3345069154d5SJulian Elischer * to work out what some of the fields should be. 3346069154d5SJulian Elischer * Hook and node references will be removed when the item is dequeued. 3347069154d5SJulian Elischer * (or equivalent) 3348069154d5SJulian Elischer */ 3349069154d5SJulian Elischer item_p 335042282202SGleb Smirnoff ng_package_msg(struct ng_mesg *msg, int flags) 3351069154d5SJulian Elischer { 3352069154d5SJulian Elischer item_p item; 3353069154d5SJulian Elischer 335442282202SGleb Smirnoff if ((item = ng_getqblk(flags)) == NULL) { 3355069154d5SJulian Elischer NG_FREE_MSG(msg); 3356069154d5SJulian Elischer return (NULL); 3357069154d5SJulian Elischer } 335830400f03SJulian Elischer ITEM_DEBUG_CHECKS; 33596f683eeeSGleb Smirnoff /* Messages items count as writers unless explicitly exempted. */ 33606f683eeeSGleb Smirnoff if (msg->header.cmd & NGM_READONLY) 33616f683eeeSGleb Smirnoff item->el_flags = NGQF_MESG | NGQF_READER; 33626f683eeeSGleb Smirnoff else 33636f683eeeSGleb Smirnoff item->el_flags = NGQF_MESG | NGQF_WRITER; 3364069154d5SJulian Elischer /* 3365069154d5SJulian Elischer * Set the current lasthook into the queue item 3366069154d5SJulian Elischer */ 3367069154d5SJulian Elischer NGI_MSG(item) = msg; 3368facfd889SArchie Cobbs NGI_RETADDR(item) = 0; 3369069154d5SJulian Elischer return (item); 3370069154d5SJulian Elischer } 3371069154d5SJulian Elischer 3372069154d5SJulian Elischer 3373069154d5SJulian Elischer 33741acb27c6SJulian Elischer #define SET_RETADDR(item, here, retaddr) \ 33756b795970SJulian Elischer do { /* Data or fn items don't have retaddrs */ \ 33766b795970SJulian Elischer if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) { \ 3377069154d5SJulian Elischer if (retaddr) { \ 3378069154d5SJulian Elischer NGI_RETADDR(item) = retaddr; \ 33794cf49a43SJulian Elischer } else { \ 3380069154d5SJulian Elischer /* \ 3381069154d5SJulian Elischer * The old return address should be ok. \ 3382069154d5SJulian Elischer * If there isn't one, use the address \ 3383069154d5SJulian Elischer * here. \ 3384069154d5SJulian Elischer */ \ 3385069154d5SJulian Elischer if (NGI_RETADDR(item) == 0) { \ 3386069154d5SJulian Elischer NGI_RETADDR(item) \ 3387069154d5SJulian Elischer = ng_node2ID(here); \ 3388069154d5SJulian Elischer } \ 3389069154d5SJulian Elischer } \ 33904cf49a43SJulian Elischer } \ 33914cf49a43SJulian Elischer } while (0) 33924cf49a43SJulian Elischer 33934cf49a43SJulian Elischer int 3394069154d5SJulian Elischer ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr) 33954cf49a43SJulian Elischer { 33961acb27c6SJulian Elischer hook_p peer; 33971acb27c6SJulian Elischer node_p peernode; 339830400f03SJulian Elischer ITEM_DEBUG_CHECKS; 3399069154d5SJulian Elischer /* 3400069154d5SJulian Elischer * Quick sanity check.. 340130400f03SJulian Elischer * Since a hook holds a reference on it's node, once we know 340230400f03SJulian Elischer * that the peer is still connected (even if invalid,) we know 340330400f03SJulian Elischer * that the peer node is present, though maybe invalid. 3404069154d5SJulian Elischer */ 3405069154d5SJulian Elischer if ((hook == NULL) 340630400f03SJulian Elischer || NG_HOOK_NOT_VALID(hook) 340730400f03SJulian Elischer || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook)) 340830400f03SJulian Elischer || NG_NODE_NOT_VALID(NG_PEER_NODE(hook))) { 3409069154d5SJulian Elischer NG_FREE_ITEM(item); 34106b795970SJulian Elischer TRAP_ERROR(); 3411e08d3e3cSJulian Elischer return (ENETDOWN); 34124cf49a43SJulian Elischer } 34134cf49a43SJulian Elischer 34144cf49a43SJulian Elischer /* 3415069154d5SJulian Elischer * Transfer our interest to the other (peer) end. 34164cf49a43SJulian Elischer */ 34171acb27c6SJulian Elischer peer = NG_HOOK_PEER(hook); 34181acb27c6SJulian Elischer NG_HOOK_REF(peer); 34191acb27c6SJulian Elischer NGI_SET_HOOK(item, peer); 34201acb27c6SJulian Elischer peernode = NG_PEER_NODE(hook); 34211acb27c6SJulian Elischer NG_NODE_REF(peernode); 34221acb27c6SJulian Elischer NGI_SET_NODE(item, peernode); 34238b68f82fSJulian Elischer SET_RETADDR(item, here, retaddr); 3424069154d5SJulian Elischer return (0); 3425069154d5SJulian Elischer } 3426069154d5SJulian Elischer 34274cf49a43SJulian Elischer int 3428069154d5SJulian Elischer ng_address_path(node_p here, item_p item, char *address, ng_ID_t retaddr) 34294cf49a43SJulian Elischer { 34304cf49a43SJulian Elischer node_p dest = NULL; 3431069154d5SJulian Elischer hook_p hook = NULL; 34324cf49a43SJulian Elischer int error; 3433069154d5SJulian Elischer 343430400f03SJulian Elischer ITEM_DEBUG_CHECKS; 3435069154d5SJulian Elischer /* 3436069154d5SJulian Elischer * Note that ng_path2noderef increments the reference count 3437069154d5SJulian Elischer * on the node for us if it finds one. So we don't have to. 3438069154d5SJulian Elischer */ 3439069154d5SJulian Elischer error = ng_path2noderef(here, address, &dest, &hook); 3440069154d5SJulian Elischer if (error) { 3441069154d5SJulian Elischer NG_FREE_ITEM(item); 344230400f03SJulian Elischer return (error); 3443069154d5SJulian Elischer } 34441acb27c6SJulian Elischer NGI_SET_NODE(item, dest); 34451acb27c6SJulian Elischer if ( hook) { 344630400f03SJulian Elischer NG_HOOK_REF(hook); /* don't let it go while on the queue */ 34471acb27c6SJulian Elischer NGI_SET_HOOK(item, hook); 34481acb27c6SJulian Elischer } 34491acb27c6SJulian Elischer SET_RETADDR(item, here, retaddr); 3450069154d5SJulian Elischer return (0); 3451069154d5SJulian Elischer } 3452069154d5SJulian Elischer 3453069154d5SJulian Elischer int 3454069154d5SJulian Elischer ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr) 3455069154d5SJulian Elischer { 3456069154d5SJulian Elischer node_p dest; 3457069154d5SJulian Elischer 345830400f03SJulian Elischer ITEM_DEBUG_CHECKS; 3459069154d5SJulian Elischer /* 3460069154d5SJulian Elischer * Find the target node. 3461069154d5SJulian Elischer */ 3462069154d5SJulian Elischer dest = ng_ID2noderef(ID); /* GETS REFERENCE! */ 3463069154d5SJulian Elischer if (dest == NULL) { 3464069154d5SJulian Elischer NG_FREE_ITEM(item); 34656b795970SJulian Elischer TRAP_ERROR(); 3466069154d5SJulian Elischer return(EINVAL); 3467069154d5SJulian Elischer } 3468069154d5SJulian Elischer /* Fill out the contents */ 34691acb27c6SJulian Elischer NGI_SET_NODE(item, dest); 34701acb27c6SJulian Elischer NGI_CLR_HOOK(item); 34711acb27c6SJulian Elischer SET_RETADDR(item, here, retaddr); 3472069154d5SJulian Elischer return (0); 3473069154d5SJulian Elischer } 3474069154d5SJulian Elischer 3475069154d5SJulian Elischer /* 3476069154d5SJulian Elischer * special case to send a message to self (e.g. destroy node) 3477069154d5SJulian Elischer * Possibly indicate an arrival hook too. 3478069154d5SJulian Elischer * Useful for removing that hook :-) 3479069154d5SJulian Elischer */ 3480069154d5SJulian Elischer item_p 3481069154d5SJulian Elischer ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg) 3482069154d5SJulian Elischer { 3483069154d5SJulian Elischer item_p item; 34844cf49a43SJulian Elischer 3485859a4d16SJulian Elischer /* 3486859a4d16SJulian Elischer * Find the target node. 3487859a4d16SJulian Elischer * If there is a HOOK argument, then use that in preference 3488859a4d16SJulian Elischer * to the address. 3489859a4d16SJulian Elischer */ 349042282202SGleb Smirnoff if ((item = ng_getqblk(NG_NOFLAGS)) == NULL) { 3491069154d5SJulian Elischer NG_FREE_MSG(msg); 3492069154d5SJulian Elischer return (NULL); 34934cf49a43SJulian Elischer } 34944cf49a43SJulian Elischer 34954cf49a43SJulian Elischer /* Fill out the contents */ 34966f683eeeSGleb Smirnoff item->el_flags = NGQF_MESG | NGQF_WRITER; 349730400f03SJulian Elischer NG_NODE_REF(here); 34981acb27c6SJulian Elischer NGI_SET_NODE(item, here); 34991acb27c6SJulian Elischer if (hook) { 350030400f03SJulian Elischer NG_HOOK_REF(hook); 35011acb27c6SJulian Elischer NGI_SET_HOOK(item, hook); 35021acb27c6SJulian Elischer } 3503069154d5SJulian Elischer NGI_MSG(item) = msg; 3504069154d5SJulian Elischer NGI_RETADDR(item) = ng_node2ID(here); 3505069154d5SJulian Elischer return (item); 35064cf49a43SJulian Elischer } 35074cf49a43SJulian Elischer 3508e088dd4cSAlexander Motin /* 3509e088dd4cSAlexander Motin * Send ng_item_fn function call to the specified node. 3510e088dd4cSAlexander Motin */ 3511e088dd4cSAlexander Motin 351242282202SGleb Smirnoff int 3513b332b91fSGleb Smirnoff ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2) 3514b332b91fSGleb Smirnoff { 3515b332b91fSGleb Smirnoff 3516b332b91fSGleb Smirnoff return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS); 3517b332b91fSGleb Smirnoff } 3518b332b91fSGleb Smirnoff 3519b332b91fSGleb Smirnoff int 3520aacdb114SGleb Smirnoff ng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2, 352142282202SGleb Smirnoff int flags) 35226b795970SJulian Elischer { 35236b795970SJulian Elischer item_p item; 35246b795970SJulian Elischer 352542282202SGleb Smirnoff if ((item = ng_getqblk(flags)) == NULL) { 35266b795970SJulian Elischer return (ENOMEM); 35276b795970SJulian Elischer } 35286b795970SJulian Elischer item->el_flags = NGQF_FN | NGQF_WRITER; 3529a96dcd84SJulian Elischer NG_NODE_REF(node); /* and one for the item */ 35301acb27c6SJulian Elischer NGI_SET_NODE(item, node); 35311acb27c6SJulian Elischer if (hook) { 35326b795970SJulian Elischer NG_HOOK_REF(hook); 35331acb27c6SJulian Elischer NGI_SET_HOOK(item, hook); 35346b795970SJulian Elischer } 35356b795970SJulian Elischer NGI_FN(item) = fn; 35366b795970SJulian Elischer NGI_ARG1(item) = arg1; 35376b795970SJulian Elischer NGI_ARG2(item) = arg2; 353842282202SGleb Smirnoff return(ng_snd_item(item, flags)); 35396b795970SJulian Elischer } 35406b795970SJulian Elischer 35414cf49a43SJulian Elischer /* 3542b332b91fSGleb Smirnoff * Send ng_item_fn2 function call to the specified node. 3543b332b91fSGleb Smirnoff * 3544b332b91fSGleb Smirnoff * If an optional pitem parameter is supplied, its apply 3545b332b91fSGleb Smirnoff * callback will be copied to the new item. If also NG_REUSE_ITEM 3546b332b91fSGleb Smirnoff * flag is set, no new item will be allocated, but pitem will 3547b332b91fSGleb Smirnoff * be used. 3548e088dd4cSAlexander Motin */ 3549e088dd4cSAlexander Motin int 3550b332b91fSGleb Smirnoff ng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1, 3551b332b91fSGleb Smirnoff int arg2, int flags) 3552e088dd4cSAlexander Motin { 3553e088dd4cSAlexander Motin item_p item; 3554e088dd4cSAlexander Motin 3555b332b91fSGleb Smirnoff KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0), 3556b332b91fSGleb Smirnoff ("%s: NG_REUSE_ITEM but no pitem", __func__)); 3557e088dd4cSAlexander Motin 3558e088dd4cSAlexander Motin /* 3559b332b91fSGleb Smirnoff * Allocate a new item if no supplied or 3560b332b91fSGleb Smirnoff * if we can't use supplied one. 3561e088dd4cSAlexander Motin */ 3562b332b91fSGleb Smirnoff if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) { 3563b332b91fSGleb Smirnoff if ((item = ng_getqblk(flags)) == NULL) 3564e088dd4cSAlexander Motin return (ENOMEM); 3565ed75521fSAlexander Motin } else { 3566ed75521fSAlexander Motin if ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA) 3567ed75521fSAlexander Motin atomic_subtract_int(&useddata, 1); 3568b332b91fSGleb Smirnoff item = pitem; 3569ed75521fSAlexander Motin } 3570b332b91fSGleb Smirnoff 3571e088dd4cSAlexander Motin item->el_flags = NGQF_FN2 | NGQF_WRITER; 3572e088dd4cSAlexander Motin NG_NODE_REF(node); /* and one for the item */ 3573e088dd4cSAlexander Motin NGI_SET_NODE(item, node); 3574e088dd4cSAlexander Motin if (hook) { 3575e088dd4cSAlexander Motin NG_HOOK_REF(hook); 3576e088dd4cSAlexander Motin NGI_SET_HOOK(item, hook); 3577e088dd4cSAlexander Motin } 3578e088dd4cSAlexander Motin NGI_FN2(item) = fn; 3579e088dd4cSAlexander Motin NGI_ARG1(item) = arg1; 3580e088dd4cSAlexander Motin NGI_ARG2(item) = arg2; 3581b332b91fSGleb Smirnoff if (pitem != NULL && (flags & NG_REUSE_ITEM) == 0) 3582e088dd4cSAlexander Motin item->apply = pitem->apply; 3583e088dd4cSAlexander Motin return(ng_snd_item(item, flags)); 3584e088dd4cSAlexander Motin } 3585e088dd4cSAlexander Motin 3586e088dd4cSAlexander Motin /* 3587d2ca21a9SJulian Elischer * Official timeout routines for Netgraph nodes. 3588d2ca21a9SJulian Elischer */ 3589d2ca21a9SJulian Elischer static void 35901fbb36ffSGleb Smirnoff ng_callout_trampoline(void *arg) 3591d2ca21a9SJulian Elischer { 3592d2ca21a9SJulian Elischer item_p item = arg; 3593d2ca21a9SJulian Elischer 3594d2ca21a9SJulian Elischer ng_snd_item(item, 0); 3595d2ca21a9SJulian Elischer } 3596d2ca21a9SJulian Elischer 3597d2ca21a9SJulian Elischer 359830bef41bSGleb Smirnoff int 3599f9d9e1b4SGleb Smirnoff ng_callout(struct callout *c, node_p node, hook_p hook, int ticks, 3600d2ca21a9SJulian Elischer ng_item_fn *fn, void * arg1, int arg2) 3601d2ca21a9SJulian Elischer { 36021bf8e0faSGleb Smirnoff item_p item, oitem; 3603d2ca21a9SJulian Elischer 360442282202SGleb Smirnoff if ((item = ng_getqblk(NG_NOFLAGS)) == NULL) 360530bef41bSGleb Smirnoff return (ENOMEM); 360630bef41bSGleb Smirnoff 3607d2ca21a9SJulian Elischer item->el_flags = NGQF_FN | NGQF_WRITER; 3608d2ca21a9SJulian Elischer NG_NODE_REF(node); /* and one for the item */ 3609d2ca21a9SJulian Elischer NGI_SET_NODE(item, node); 3610d2ca21a9SJulian Elischer if (hook) { 3611d2ca21a9SJulian Elischer NG_HOOK_REF(hook); 3612d2ca21a9SJulian Elischer NGI_SET_HOOK(item, hook); 3613d2ca21a9SJulian Elischer } 3614d2ca21a9SJulian Elischer NGI_FN(item) = fn; 3615d2ca21a9SJulian Elischer NGI_ARG1(item) = arg1; 3616d2ca21a9SJulian Elischer NGI_ARG2(item) = arg2; 36171bf8e0faSGleb Smirnoff oitem = c->c_arg; 36181bf8e0faSGleb Smirnoff if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 && 36191bf8e0faSGleb Smirnoff oitem != NULL) 36201bf8e0faSGleb Smirnoff NG_FREE_ITEM(oitem); 362130bef41bSGleb Smirnoff return (0); 3622d2ca21a9SJulian Elischer } 3623d2ca21a9SJulian Elischer 3624d2ca21a9SJulian Elischer /* A special modified version of untimeout() */ 3625d2ca21a9SJulian Elischer int 3626f9d9e1b4SGleb Smirnoff ng_uncallout(struct callout *c, node_p node) 3627d2ca21a9SJulian Elischer { 3628d2ca21a9SJulian Elischer item_p item; 362930bef41bSGleb Smirnoff int rval; 3630d2ca21a9SJulian Elischer 363103b25f5dSGleb Smirnoff KASSERT(c != NULL, ("ng_uncallout: NULL callout")); 363203b25f5dSGleb Smirnoff KASSERT(node != NULL, ("ng_uncallout: NULL node")); 363303b25f5dSGleb Smirnoff 36343eadb26dSGleb Smirnoff rval = callout_stop(c); 363530bef41bSGleb Smirnoff item = c->c_arg; 363630bef41bSGleb Smirnoff /* Do an extra check */ 36371fbb36ffSGleb Smirnoff if ((rval > 0) && (c->c_func == &ng_callout_trampoline) && 363830bef41bSGleb Smirnoff (NGI_NODE(item) == node)) { 3639d2ca21a9SJulian Elischer /* 3640d2ca21a9SJulian Elischer * We successfully removed it from the queue before it ran 3641d2ca21a9SJulian Elischer * So now we need to unreference everything that was 3642d2ca21a9SJulian Elischer * given extra references. (NG_FREE_ITEM does this). 3643d2ca21a9SJulian Elischer */ 3644d2ca21a9SJulian Elischer NG_FREE_ITEM(item); 3645d2ca21a9SJulian Elischer } 36461bf8e0faSGleb Smirnoff c->c_arg = NULL; 364730bef41bSGleb Smirnoff 364830bef41bSGleb Smirnoff return (rval); 3649d2ca21a9SJulian Elischer } 3650d2ca21a9SJulian Elischer 3651d2ca21a9SJulian Elischer /* 3652069154d5SJulian Elischer * Set the address, if none given, give the node here. 36534cf49a43SJulian Elischer */ 3654069154d5SJulian Elischer void 3655069154d5SJulian Elischer ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr) 36564cf49a43SJulian Elischer { 3657069154d5SJulian Elischer if (retaddr) { 3658069154d5SJulian Elischer NGI_RETADDR(item) = retaddr; 3659069154d5SJulian Elischer } else { 3660069154d5SJulian Elischer /* 3661069154d5SJulian Elischer * The old return address should be ok. 3662069154d5SJulian Elischer * If there isn't one, use the address here. 3663069154d5SJulian Elischer */ 3664069154d5SJulian Elischer NGI_RETADDR(item) = ng_node2ID(here); 3665069154d5SJulian Elischer } 3666069154d5SJulian Elischer } 3667069154d5SJulian Elischer 3668069154d5SJulian Elischer #define TESTING 3669069154d5SJulian Elischer #ifdef TESTING 3670069154d5SJulian Elischer /* just test all the macros */ 3671069154d5SJulian Elischer void 3672069154d5SJulian Elischer ng_macro_test(item_p item); 3673069154d5SJulian Elischer void 3674069154d5SJulian Elischer ng_macro_test(item_p item) 3675069154d5SJulian Elischer { 3676069154d5SJulian Elischer node_p node = NULL; 3677069154d5SJulian Elischer hook_p hook = NULL; 36784cf49a43SJulian Elischer struct mbuf *m; 36794cf49a43SJulian Elischer struct ng_mesg *msg; 3680069154d5SJulian Elischer ng_ID_t retaddr; 3681069154d5SJulian Elischer int error; 36824cf49a43SJulian Elischer 3683069154d5SJulian Elischer NGI_GET_M(item, m); 3684069154d5SJulian Elischer NGI_GET_MSG(item, msg); 3685069154d5SJulian Elischer retaddr = NGI_RETADDR(item); 36868ed370fdSJulian Elischer NG_SEND_DATA(error, hook, m, NULL); 3687069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, hook, m); 3688069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, hook, m); 368930400f03SJulian Elischer NG_FWD_ITEM_HOOK(error, item, hook); 3690069154d5SJulian Elischer NG_SEND_MSG_HOOK(error, node, msg, hook, retaddr); 3691069154d5SJulian Elischer NG_SEND_MSG_ID(error, node, msg, retaddr, retaddr); 3692069154d5SJulian Elischer NG_SEND_MSG_PATH(error, node, msg, ".:", retaddr); 3693069154d5SJulian Elischer NG_FWD_MSG_HOOK(error, node, item, hook, retaddr); 36944cf49a43SJulian Elischer } 3695069154d5SJulian Elischer #endif /* TESTING */ 36964cf49a43SJulian Elischer 3697