1c398230bSWarner Losh /*- 24cf49a43SJulian Elischer * Copyright (c) 1996-1999 Whistle Communications, Inc. 34cf49a43SJulian Elischer * All rights reserved. 44cf49a43SJulian Elischer * 54cf49a43SJulian Elischer * Subject to the following obligations and disclaimer of warranty, use and 64cf49a43SJulian Elischer * redistribution of this software, in source or object code forms, with or 74cf49a43SJulian Elischer * without modifications are expressly permitted by Whistle Communications; 84cf49a43SJulian Elischer * provided, however, that: 94cf49a43SJulian Elischer * 1. Any and all reproductions of the source or object code must include the 104cf49a43SJulian Elischer * copyright notice above and the following disclaimer of warranties; and 114cf49a43SJulian Elischer * 2. No rights are granted, in any manner or form, to use Whistle 124cf49a43SJulian Elischer * Communications, Inc. trademarks, including the mark "WHISTLE 134cf49a43SJulian Elischer * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 144cf49a43SJulian Elischer * such appears in the above copyright notice or in the software. 154cf49a43SJulian Elischer * 164cf49a43SJulian Elischer * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 174cf49a43SJulian Elischer * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 184cf49a43SJulian Elischer * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 194cf49a43SJulian Elischer * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 204cf49a43SJulian Elischer * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 214cf49a43SJulian Elischer * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 224cf49a43SJulian Elischer * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 234cf49a43SJulian Elischer * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 244cf49a43SJulian Elischer * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 254cf49a43SJulian Elischer * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 264cf49a43SJulian Elischer * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 274cf49a43SJulian Elischer * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 284cf49a43SJulian Elischer * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 294cf49a43SJulian Elischer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 304cf49a43SJulian Elischer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 314cf49a43SJulian Elischer * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 324cf49a43SJulian Elischer * OF SUCH DAMAGE. 334cf49a43SJulian Elischer * 34cc3bbd68SJulian Elischer * Authors: Julian Elischer <julian@freebsd.org> 35cc3bbd68SJulian Elischer * Archie Cobbs <archie@freebsd.org> 364cf49a43SJulian Elischer * 374cf49a43SJulian Elischer * $FreeBSD$ 384cf49a43SJulian Elischer * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $ 394cf49a43SJulian Elischer */ 404cf49a43SJulian Elischer 414cf49a43SJulian Elischer /* 424cf49a43SJulian Elischer * This file implements the base netgraph code. 434cf49a43SJulian Elischer */ 444cf49a43SJulian Elischer 454cf49a43SJulian Elischer #include <sys/param.h> 46b99a7379SGleb Smirnoff #include <sys/systm.h> 4777a58296SGleb Smirnoff #include <sys/ctype.h> 48687adb70SGleb Smirnoff #include <sys/hash.h> 49f33ca0c9SMarcel Moolenaar #include <sys/kdb.h> 504cf49a43SJulian Elischer #include <sys/kernel.h> 5119afcd98SGleb Smirnoff #include <sys/kthread.h> 523b33fbe7SGleb Smirnoff #include <sys/ktr.h> 53104a9b7eSAlexander Kabaev #include <sys/limits.h> 54c4282b74SGleb Smirnoff #include <sys/lock.h> 554cf49a43SJulian Elischer #include <sys/malloc.h> 564cf49a43SJulian Elischer #include <sys/mbuf.h> 5719afcd98SGleb Smirnoff #include <sys/proc.h> 5877a58296SGleb Smirnoff #include <sys/queue.h> 5919afcd98SGleb Smirnoff #include <sys/refcount.h> 6019afcd98SGleb Smirnoff #include <sys/rwlock.h> 6119afcd98SGleb Smirnoff #include <sys/smp.h> 62bfa7e882SJulian Elischer #include <sys/sysctl.h> 6377a58296SGleb Smirnoff #include <sys/syslog.h> 64f2fbb838SAlexander Motin #include <sys/unistd.h> 65394cb30aSAlexander Motin #include <machine/cpu.h> 664cf49a43SJulian Elischer 674cf49a43SJulian Elischer #include <net/netisr.h> 68eddfbb76SRobert Watson #include <net/vnet.h> 694cf49a43SJulian Elischer 704cf49a43SJulian Elischer #include <netgraph/ng_message.h> 714cf49a43SJulian Elischer #include <netgraph/netgraph.h> 72f8307e12SArchie Cobbs #include <netgraph/ng_parse.h> 734cf49a43SJulian Elischer 749d72a7a3SJulian Elischer MODULE_VERSION(netgraph, NG_ABI_VERSION); 7599ff8176SPeter Wemm 76ac5dd141SGleb Smirnoff /* Mutex to protect topology events. */ 77*d2fd0788SAlexander V. Chernikov static struct rwlock ng_topo_lock; 78*d2fd0788SAlexander V. Chernikov #define TOPOLOGY_RLOCK() rw_rlock(&ng_topo_lock) 79*d2fd0788SAlexander V. Chernikov #define TOPOLOGY_RUNLOCK() rw_runlock(&ng_topo_lock) 80*d2fd0788SAlexander V. Chernikov #define TOPOLOGY_WLOCK() rw_wlock(&ng_topo_lock) 81*d2fd0788SAlexander V. Chernikov #define TOPOLOGY_WUNLOCK() rw_wunlock(&ng_topo_lock) 82*d2fd0788SAlexander V. Chernikov #define TOPOLOGY_NOTOWNED() rw_assert(&ng_topo_lock, RA_UNLOCKED) 83ac5dd141SGleb Smirnoff 8430400f03SJulian Elischer #ifdef NETGRAPH_DEBUG 85cfea3f85SAlexander Motin static struct mtx ng_nodelist_mtx; /* protects global node/hook lists */ 861489164fSGleb Smirnoff static struct mtx ngq_mtx; /* protects the queue item list */ 8730400f03SJulian Elischer 8830400f03SJulian Elischer static SLIST_HEAD(, ng_node) ng_allnodes; 8930400f03SJulian Elischer static LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */ 9030400f03SJulian Elischer static SLIST_HEAD(, ng_hook) ng_allhooks; 9130400f03SJulian Elischer static LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */ 9230400f03SJulian Elischer 9330400f03SJulian Elischer static void ng_dumpitems(void); 9430400f03SJulian Elischer static void ng_dumpnodes(void); 9530400f03SJulian Elischer static void ng_dumphooks(void); 9630400f03SJulian Elischer 9730400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ 98954c4772SJulian Elischer /* 99954c4772SJulian Elischer * DEAD versions of the structures. 100a2098feaSGabor Kovesdan * In order to avoid races, it is sometimes necessary to point 101954c4772SJulian Elischer * at SOMETHING even though theoretically, the current entity is 102954c4772SJulian Elischer * INVALID. Use these to avoid these races. 103954c4772SJulian Elischer */ 104954c4772SJulian Elischer struct ng_type ng_deadtype = { 105954c4772SJulian Elischer NG_ABI_VERSION, 106954c4772SJulian Elischer "dead", 107954c4772SJulian Elischer NULL, /* modevent */ 108954c4772SJulian Elischer NULL, /* constructor */ 109954c4772SJulian Elischer NULL, /* rcvmsg */ 110954c4772SJulian Elischer NULL, /* shutdown */ 111954c4772SJulian Elischer NULL, /* newhook */ 112954c4772SJulian Elischer NULL, /* findhook */ 113954c4772SJulian Elischer NULL, /* connect */ 114954c4772SJulian Elischer NULL, /* rcvdata */ 115954c4772SJulian Elischer NULL, /* disconnect */ 116954c4772SJulian Elischer NULL, /* cmdlist */ 117954c4772SJulian Elischer }; 11830400f03SJulian Elischer 119954c4772SJulian Elischer struct ng_node ng_deadnode = { 120954c4772SJulian Elischer "dead", 121954c4772SJulian Elischer &ng_deadtype, 122be4252b3SJulian Elischer NGF_INVALID, 123954c4772SJulian Elischer 0, /* numhooks */ 124954c4772SJulian Elischer NULL, /* private */ 125954c4772SJulian Elischer 0, /* ID */ 12613e403fdSAntoine Brodin LIST_HEAD_INITIALIZER(ng_deadnode.nd_hooks), 127954c4772SJulian Elischer {}, /* all_nodes list entry */ 128954c4772SJulian Elischer {}, /* id hashtable list entry */ 129954c4772SJulian Elischer { 0, 1309852972bSAlexander Motin 0, 131954c4772SJulian Elischer {}, /* should never use! (should hang) */ 1329852972bSAlexander Motin {}, /* workqueue entry */ 1339852972bSAlexander Motin STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue), 134954c4772SJulian Elischer }, 1359852972bSAlexander Motin 1, /* refs */ 136a40b7874SMarko Zec NULL, /* vnet */ 137954c4772SJulian Elischer #ifdef NETGRAPH_DEBUG 138954c4772SJulian Elischer ND_MAGIC, 139954c4772SJulian Elischer __FILE__, 140954c4772SJulian Elischer __LINE__, 141954c4772SJulian Elischer {NULL} 142954c4772SJulian Elischer #endif /* NETGRAPH_DEBUG */ 143954c4772SJulian Elischer }; 144954c4772SJulian Elischer 145954c4772SJulian Elischer struct ng_hook ng_deadhook = { 146954c4772SJulian Elischer "dead", 147954c4772SJulian Elischer NULL, /* private */ 148954c4772SJulian Elischer HK_INVALID | HK_DEAD, 149e58d779dSGleb Smirnoff 0, /* undefined data link type */ 150954c4772SJulian Elischer &ng_deadhook, /* Peer is self */ 151954c4772SJulian Elischer &ng_deadnode, /* attached to deadnode */ 152954c4772SJulian Elischer {}, /* hooks list */ 153c4b5eea4SJulian Elischer NULL, /* override rcvmsg() */ 154c4b5eea4SJulian Elischer NULL, /* override rcvdata() */ 1559852972bSAlexander Motin 1, /* refs always >= 1 */ 156954c4772SJulian Elischer #ifdef NETGRAPH_DEBUG 157954c4772SJulian Elischer HK_MAGIC, 158954c4772SJulian Elischer __FILE__, 159954c4772SJulian Elischer __LINE__, 160954c4772SJulian Elischer {NULL} 161954c4772SJulian Elischer #endif /* NETGRAPH_DEBUG */ 162954c4772SJulian Elischer }; 163954c4772SJulian Elischer 164954c4772SJulian Elischer /* 165954c4772SJulian Elischer * END DEAD STRUCTURES 166954c4772SJulian Elischer */ 167069154d5SJulian Elischer /* List nodes with unallocated work */ 1689852972bSAlexander Motin static STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist); 169b57a7965SJulian Elischer static struct mtx ng_worklist_mtx; /* MUST LOCK NODE FIRST */ 1704cf49a43SJulian Elischer 1714cf49a43SJulian Elischer /* List of installed types */ 172069154d5SJulian Elischer static LIST_HEAD(, ng_type) ng_typelist; 173c4282b74SGleb Smirnoff static struct rwlock ng_typelist_lock; 174c4282b74SGleb Smirnoff #define TYPELIST_RLOCK() rw_rlock(&ng_typelist_lock) 175c4282b74SGleb Smirnoff #define TYPELIST_RUNLOCK() rw_runlock(&ng_typelist_lock) 176c4282b74SGleb Smirnoff #define TYPELIST_WLOCK() rw_wlock(&ng_typelist_lock) 177c4282b74SGleb Smirnoff #define TYPELIST_WUNLOCK() rw_wunlock(&ng_typelist_lock) 1784cf49a43SJulian Elischer 179687adb70SGleb Smirnoff /* Hash related definitions. */ 180687adb70SGleb Smirnoff LIST_HEAD(nodehash, ng_node); 181687adb70SGleb Smirnoff static VNET_DEFINE(struct nodehash *, ng_ID_hash); 182687adb70SGleb Smirnoff static VNET_DEFINE(u_long, ng_ID_hmask); 183687adb70SGleb Smirnoff static VNET_DEFINE(u_long, ng_nodes); 184687adb70SGleb Smirnoff static VNET_DEFINE(struct nodehash *, ng_name_hash); 185687adb70SGleb Smirnoff static VNET_DEFINE(u_long, ng_name_hmask); 186687adb70SGleb Smirnoff static VNET_DEFINE(u_long, ng_named_nodes); 1871e77c105SRobert Watson #define V_ng_ID_hash VNET(ng_ID_hash) 188687adb70SGleb Smirnoff #define V_ng_ID_hmask VNET(ng_ID_hmask) 189687adb70SGleb Smirnoff #define V_ng_nodes VNET(ng_nodes) 190687adb70SGleb Smirnoff #define V_ng_name_hash VNET(ng_name_hash) 191687adb70SGleb Smirnoff #define V_ng_name_hmask VNET(ng_name_hmask) 192687adb70SGleb Smirnoff #define V_ng_named_nodes VNET(ng_named_nodes) 193eddfbb76SRobert Watson 194c4282b74SGleb Smirnoff static struct rwlock ng_idhash_lock; 195c4282b74SGleb Smirnoff #define IDHASH_RLOCK() rw_rlock(&ng_idhash_lock) 196c4282b74SGleb Smirnoff #define IDHASH_RUNLOCK() rw_runlock(&ng_idhash_lock) 197c4282b74SGleb Smirnoff #define IDHASH_WLOCK() rw_wlock(&ng_idhash_lock) 198c4282b74SGleb Smirnoff #define IDHASH_WUNLOCK() rw_wunlock(&ng_idhash_lock) 199c4282b74SGleb Smirnoff 2000f150d04SJulian Elischer /* Method to find a node.. used twice so do it here */ 201687adb70SGleb Smirnoff #define NG_IDHASH_FN(ID) ((ID) % (V_ng_ID_hmask + 1)) 2020f150d04SJulian Elischer #define NG_IDHASH_FIND(ID, node) \ 2030f150d04SJulian Elischer do { \ 204c4282b74SGleb Smirnoff rw_assert(&ng_idhash_lock, RA_LOCKED); \ 205603724d3SBjoern A. Zeeb LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)], \ 2060f150d04SJulian Elischer nd_idnodes) { \ 2070f150d04SJulian Elischer if (NG_NODE_IS_VALID(node) \ 2080f150d04SJulian Elischer && (NG_NODE_ID(node) == ID)) { \ 2090f150d04SJulian Elischer break; \ 2100f150d04SJulian Elischer } \ 2110f150d04SJulian Elischer } \ 2120f150d04SJulian Elischer } while (0) 213069154d5SJulian Elischer 214c4282b74SGleb Smirnoff static struct rwlock ng_namehash_lock; 215c4282b74SGleb Smirnoff #define NAMEHASH_RLOCK() rw_rlock(&ng_namehash_lock) 216c4282b74SGleb Smirnoff #define NAMEHASH_RUNLOCK() rw_runlock(&ng_namehash_lock) 217c4282b74SGleb Smirnoff #define NAMEHASH_WLOCK() rw_wlock(&ng_namehash_lock) 218c4282b74SGleb Smirnoff #define NAMEHASH_WUNLOCK() rw_wunlock(&ng_namehash_lock) 219dc90cad9SJulian Elischer 2204cf49a43SJulian Elischer /* Internal functions */ 2214cf49a43SJulian Elischer static int ng_add_hook(node_p node, const char *name, hook_p * hookp); 222069154d5SJulian Elischer static int ng_generic_msg(node_p here, item_p item, hook_p lasthook); 223dc90cad9SJulian Elischer static ng_ID_t ng_decodeidname(const char *name); 2244cf49a43SJulian Elischer static int ngb_mod_event(module_t mod, int event, void *data); 225394cb30aSAlexander Motin static void ng_worklist_add(node_p node); 226f2fbb838SAlexander Motin static void ngthread(void *); 22727757487SGleb Smirnoff static int ng_apply_item(node_p node, item_p item, int rw); 2289852972bSAlexander Motin static void ng_flush_input_queue(node_p node); 229069154d5SJulian Elischer static node_p ng_ID2noderef(ng_ID_t ID); 230e088dd4cSAlexander Motin static int ng_con_nodes(item_p item, node_p node, const char *name, 231e088dd4cSAlexander Motin node_p node2, const char *name2); 232e088dd4cSAlexander Motin static int ng_con_part2(node_p node, item_p item, hook_p hook); 233e088dd4cSAlexander Motin static int ng_con_part3(node_p node, item_p item, hook_p hook); 234687adb70SGleb Smirnoff static int ng_mkpeer(node_p node, const char *name, const char *name2, 235687adb70SGleb Smirnoff char *type); 236687adb70SGleb Smirnoff static void ng_name_rehash(void); 237687adb70SGleb Smirnoff static void ng_ID_rehash(void); 238069154d5SJulian Elischer 2394c9b5910SGleb Smirnoff /* Imported, these used to be externally visible, some may go back. */ 240069154d5SJulian Elischer void ng_destroy_hook(hook_p hook); 241069154d5SJulian Elischer int ng_path2noderef(node_p here, const char *path, 242069154d5SJulian Elischer node_p *dest, hook_p *lasthook); 243069154d5SJulian Elischer int ng_make_node(const char *type, node_p *nodepp); 244069154d5SJulian Elischer int ng_path_parse(char *addr, char **node, char **path, char **hook); 2451acb27c6SJulian Elischer void ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3); 24630400f03SJulian Elischer void ng_unname(node_p node); 247069154d5SJulian Elischer 2484cf49a43SJulian Elischer /* Our own netgraph malloc type */ 2494cf49a43SJulian Elischer MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); 250069154d5SJulian Elischer MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage"); 251d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", 252d745c852SEd Schouten "netgraph hook structures"); 253d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", 254d745c852SEd Schouten "netgraph node structures"); 255d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", 256d745c852SEd Schouten "netgraph item structures"); 257069154d5SJulian Elischer 258069154d5SJulian Elischer /* Should not be visible outside this file */ 25930400f03SJulian Elischer 26030400f03SJulian Elischer #define _NG_ALLOC_HOOK(hook) \ 2611ede983cSDag-Erling Smørgrav hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO) 26230400f03SJulian Elischer #define _NG_ALLOC_NODE(node) \ 2631ede983cSDag-Erling Smørgrav node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO) 26430400f03SJulian Elischer 2652c8dda8dSWojciech A. Koszek #define NG_QUEUE_LOCK_INIT(n) \ 2664abab3d5SWojciech A. Koszek mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF) 2672c8dda8dSWojciech A. Koszek #define NG_QUEUE_LOCK(n) \ 2684abab3d5SWojciech A. Koszek mtx_lock(&(n)->q_mtx) 2692c8dda8dSWojciech A. Koszek #define NG_QUEUE_UNLOCK(n) \ 2704abab3d5SWojciech A. Koszek mtx_unlock(&(n)->q_mtx) 2712c8dda8dSWojciech A. Koszek #define NG_WORKLIST_LOCK_INIT() \ 2724abab3d5SWojciech A. Koszek mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF) 2732c8dda8dSWojciech A. Koszek #define NG_WORKLIST_LOCK() \ 2744abab3d5SWojciech A. Koszek mtx_lock(&ng_worklist_mtx) 2752c8dda8dSWojciech A. Koszek #define NG_WORKLIST_UNLOCK() \ 2764abab3d5SWojciech A. Koszek mtx_unlock(&ng_worklist_mtx) 277f2fbb838SAlexander Motin #define NG_WORKLIST_SLEEP() \ 278f2fbb838SAlexander Motin mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0) 279f2fbb838SAlexander Motin #define NG_WORKLIST_WAKEUP() \ 280f2fbb838SAlexander Motin wakeup_one(&ng_worklist) 2812c8dda8dSWojciech A. Koszek 28230400f03SJulian Elischer #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ 28330400f03SJulian Elischer /* 28430400f03SJulian Elischer * In debug mode: 28530400f03SJulian Elischer * In an attempt to help track reference count screwups 28630400f03SJulian Elischer * we do not free objects back to the malloc system, but keep them 28730400f03SJulian Elischer * in a local cache where we can examine them and keep information safely 28830400f03SJulian Elischer * after they have been freed. 28930400f03SJulian Elischer * We use this scheme for nodes and hooks, and to some extent for items. 29030400f03SJulian Elischer */ 29130400f03SJulian Elischer static __inline hook_p 29230400f03SJulian Elischer ng_alloc_hook(void) 29330400f03SJulian Elischer { 29430400f03SJulian Elischer hook_p hook; 29530400f03SJulian Elischer SLIST_ENTRY(ng_hook) temp; 2969ed346baSBosko Milekic mtx_lock(&ng_nodelist_mtx); 29730400f03SJulian Elischer hook = LIST_FIRST(&ng_freehooks); 29830400f03SJulian Elischer if (hook) { 29930400f03SJulian Elischer LIST_REMOVE(hook, hk_hooks); 30030400f03SJulian Elischer bcopy(&hook->hk_all, &temp, sizeof(temp)); 30130400f03SJulian Elischer bzero(hook, sizeof(struct ng_hook)); 30230400f03SJulian Elischer bcopy(&temp, &hook->hk_all, sizeof(temp)); 3039ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); 30430400f03SJulian Elischer hook->hk_magic = HK_MAGIC; 30530400f03SJulian Elischer } else { 3069ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); 30730400f03SJulian Elischer _NG_ALLOC_HOOK(hook); 30830400f03SJulian Elischer if (hook) { 30930400f03SJulian Elischer hook->hk_magic = HK_MAGIC; 3109ed346baSBosko Milekic mtx_lock(&ng_nodelist_mtx); 31130400f03SJulian Elischer SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all); 3129ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); 31330400f03SJulian Elischer } 31430400f03SJulian Elischer } 31530400f03SJulian Elischer return (hook); 31630400f03SJulian Elischer } 31730400f03SJulian Elischer 31830400f03SJulian Elischer static __inline node_p 31930400f03SJulian Elischer ng_alloc_node(void) 32030400f03SJulian Elischer { 32130400f03SJulian Elischer node_p node; 32230400f03SJulian Elischer SLIST_ENTRY(ng_node) temp; 3239ed346baSBosko Milekic mtx_lock(&ng_nodelist_mtx); 32430400f03SJulian Elischer node = LIST_FIRST(&ng_freenodes); 32530400f03SJulian Elischer if (node) { 32630400f03SJulian Elischer LIST_REMOVE(node, nd_nodes); 32730400f03SJulian Elischer bcopy(&node->nd_all, &temp, sizeof(temp)); 32830400f03SJulian Elischer bzero(node, sizeof(struct ng_node)); 32930400f03SJulian Elischer bcopy(&temp, &node->nd_all, sizeof(temp)); 3309ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); 33130400f03SJulian Elischer node->nd_magic = ND_MAGIC; 33230400f03SJulian Elischer } else { 3339ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); 33430400f03SJulian Elischer _NG_ALLOC_NODE(node); 33530400f03SJulian Elischer if (node) { 33630400f03SJulian Elischer node->nd_magic = ND_MAGIC; 3379ed346baSBosko Milekic mtx_lock(&ng_nodelist_mtx); 33830400f03SJulian Elischer SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all); 3399ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); 34030400f03SJulian Elischer } 34130400f03SJulian Elischer } 34230400f03SJulian Elischer return (node); 34330400f03SJulian Elischer } 34430400f03SJulian Elischer 34530400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0) 34630400f03SJulian Elischer #define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0) 34730400f03SJulian Elischer 34830400f03SJulian Elischer #define NG_FREE_HOOK(hook) \ 34930400f03SJulian Elischer do { \ 3509ed346baSBosko Milekic mtx_lock(&ng_nodelist_mtx); \ 35130400f03SJulian Elischer LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks); \ 35230400f03SJulian Elischer hook->hk_magic = 0; \ 3539ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); \ 35430400f03SJulian Elischer } while (0) 35530400f03SJulian Elischer 35630400f03SJulian Elischer #define NG_FREE_NODE(node) \ 35730400f03SJulian Elischer do { \ 3589ed346baSBosko Milekic mtx_lock(&ng_nodelist_mtx); \ 35930400f03SJulian Elischer LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes); \ 36030400f03SJulian Elischer node->nd_magic = 0; \ 3619ed346baSBosko Milekic mtx_unlock(&ng_nodelist_mtx); \ 36230400f03SJulian Elischer } while (0) 36330400f03SJulian Elischer 36430400f03SJulian Elischer #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ 36530400f03SJulian Elischer 36630400f03SJulian Elischer #define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook) 36730400f03SJulian Elischer #define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node) 36830400f03SJulian Elischer 3691ede983cSDag-Erling Smørgrav #define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0) 3701ede983cSDag-Erling Smørgrav #define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0) 37130400f03SJulian Elischer 37230400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ 37330400f03SJulian Elischer 374f33ca0c9SMarcel Moolenaar /* Set this to kdb_enter("X") to catch all errors as they occur */ 3754cf49a43SJulian Elischer #ifndef TRAP_ERROR 3766b795970SJulian Elischer #define TRAP_ERROR() 3774cf49a43SJulian Elischer #endif 3784cf49a43SJulian Elischer 3793e288e62SDimitry Andric static VNET_DEFINE(ng_ID_t, nextID) = 1; 3801e77c105SRobert Watson #define V_nextID VNET(nextID) 381dc90cad9SJulian Elischer 382b2da83c2SArchie Cobbs #ifdef INVARIANTS 383b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m) do { \ 384b2da83c2SArchie Cobbs struct mbuf *n; \ 385b2da83c2SArchie Cobbs int total; \ 386b2da83c2SArchie Cobbs \ 387fe584538SDag-Erling Smørgrav M_ASSERTPKTHDR(m); \ 388b32cfb32SGleb Smirnoff for (total = 0, n = (m); n != NULL; n = n->m_next) { \ 389b2da83c2SArchie Cobbs total += n->m_len; \ 390b32cfb32SGleb Smirnoff if (n->m_nextpkt != NULL) \ 391b32cfb32SGleb Smirnoff panic("%s: m_nextpkt", __func__); \ 392b32cfb32SGleb Smirnoff } \ 393ba5b359aSGleb Smirnoff \ 394b2da83c2SArchie Cobbs if ((m)->m_pkthdr.len != total) { \ 395b2da83c2SArchie Cobbs panic("%s: %d != %d", \ 3966e551fb6SDavid E. O'Brien __func__, (m)->m_pkthdr.len, total); \ 397b2da83c2SArchie Cobbs } \ 398b2da83c2SArchie Cobbs } while (0) 399b2da83c2SArchie Cobbs #else 400b2da83c2SArchie Cobbs #define CHECK_DATA_MBUF(m) 401b2da83c2SArchie Cobbs #endif 402b2da83c2SArchie Cobbs 403e088dd4cSAlexander Motin #define ERROUT(x) do { error = (x); goto done; } while (0) 404dc90cad9SJulian Elischer 4054cf49a43SJulian Elischer /************************************************************************ 406f8307e12SArchie Cobbs Parse type definitions for generic messages 407f8307e12SArchie Cobbs ************************************************************************/ 408f8307e12SArchie Cobbs 409f8307e12SArchie Cobbs /* Handy structure parse type defining macro */ 410f8307e12SArchie Cobbs #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ 411f0184ff8SArchie Cobbs static const struct ng_parse_struct_field \ 412f0184ff8SArchie Cobbs ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args; \ 413f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_ ## lo ## _type = { \ 414f8307e12SArchie Cobbs &ng_parse_struct_type, \ 415f0184ff8SArchie Cobbs &ng_ ## lo ## _type_fields \ 416f8307e12SArchie Cobbs } 417f8307e12SArchie Cobbs 418f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); 419f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); 420f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); 421f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); 422f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); 423f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); 424f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); 425f8307e12SArchie Cobbs 426f8307e12SArchie Cobbs /* Get length of an array when the length is stored as a 32 bit 427d7d97eb0SJeroen Ruigrok van der Werven value immediately preceding the array -- as with struct namelist 428f8307e12SArchie Cobbs and struct typelist. */ 429f8307e12SArchie Cobbs static int 430f8307e12SArchie Cobbs ng_generic_list_getLength(const struct ng_parse_type *type, 431f8307e12SArchie Cobbs const u_char *start, const u_char *buf) 432f8307e12SArchie Cobbs { 433f8307e12SArchie Cobbs return *((const u_int32_t *)(buf - 4)); 434f8307e12SArchie Cobbs } 435f8307e12SArchie Cobbs 436f8307e12SArchie Cobbs /* Get length of the array of struct linkinfo inside a struct hooklist */ 437f8307e12SArchie Cobbs static int 438f8307e12SArchie Cobbs ng_generic_linkinfo_getLength(const struct ng_parse_type *type, 439f8307e12SArchie Cobbs const u_char *start, const u_char *buf) 440f8307e12SArchie Cobbs { 441f8307e12SArchie Cobbs const struct hooklist *hl = (const struct hooklist *)start; 442f8307e12SArchie Cobbs 443f8307e12SArchie Cobbs return hl->nodeinfo.hooks; 444f8307e12SArchie Cobbs } 445f8307e12SArchie Cobbs 446f8307e12SArchie Cobbs /* Array type for a variable length array of struct namelist */ 447f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_nodeinfoarray_type_info = { 448f8307e12SArchie Cobbs &ng_generic_nodeinfo_type, 449f8307e12SArchie Cobbs &ng_generic_list_getLength 450f8307e12SArchie Cobbs }; 451f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_nodeinfoarray_type = { 452f8307e12SArchie Cobbs &ng_parse_array_type, 453f8307e12SArchie Cobbs &ng_nodeinfoarray_type_info 454f8307e12SArchie Cobbs }; 455f8307e12SArchie Cobbs 456f8307e12SArchie Cobbs /* Array type for a variable length array of struct typelist */ 457f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_typeinfoarray_type_info = { 458f8307e12SArchie Cobbs &ng_generic_typeinfo_type, 459f8307e12SArchie Cobbs &ng_generic_list_getLength 460f8307e12SArchie Cobbs }; 461f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_typeinfoarray_type = { 462f8307e12SArchie Cobbs &ng_parse_array_type, 463f8307e12SArchie Cobbs &ng_typeinfoarray_type_info 464f8307e12SArchie Cobbs }; 465f8307e12SArchie Cobbs 466f8307e12SArchie Cobbs /* Array type for array of struct linkinfo in struct hooklist */ 467f8307e12SArchie Cobbs static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { 468f8307e12SArchie Cobbs &ng_generic_linkinfo_type, 469f8307e12SArchie Cobbs &ng_generic_linkinfo_getLength 470f8307e12SArchie Cobbs }; 471f8307e12SArchie Cobbs static const struct ng_parse_type ng_generic_linkinfo_array_type = { 472f8307e12SArchie Cobbs &ng_parse_array_type, 473f8307e12SArchie Cobbs &ng_generic_linkinfo_array_type_info 474f8307e12SArchie Cobbs }; 475f8307e12SArchie Cobbs 476f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); 477f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, 478f8307e12SArchie Cobbs (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); 479f8307e12SArchie Cobbs DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, 480f8307e12SArchie Cobbs (&ng_generic_nodeinfoarray_type)); 481f8307e12SArchie Cobbs 482f8307e12SArchie Cobbs /* List of commands and how to convert arguments to/from ASCII */ 483f8307e12SArchie Cobbs static const struct ng_cmdlist ng_generic_cmds[] = { 484f8307e12SArchie Cobbs { 485f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 486f8307e12SArchie Cobbs NGM_SHUTDOWN, 487f8307e12SArchie Cobbs "shutdown", 488f8307e12SArchie Cobbs NULL, 489f8307e12SArchie Cobbs NULL 490f8307e12SArchie Cobbs }, 491f8307e12SArchie Cobbs { 492f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 493f8307e12SArchie Cobbs NGM_MKPEER, 494f8307e12SArchie Cobbs "mkpeer", 495f8307e12SArchie Cobbs &ng_generic_mkpeer_type, 496f8307e12SArchie Cobbs NULL 497f8307e12SArchie Cobbs }, 498f8307e12SArchie Cobbs { 499f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 500f8307e12SArchie Cobbs NGM_CONNECT, 501f8307e12SArchie Cobbs "connect", 502f8307e12SArchie Cobbs &ng_generic_connect_type, 503f8307e12SArchie Cobbs NULL 504f8307e12SArchie Cobbs }, 505f8307e12SArchie Cobbs { 506f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 507f8307e12SArchie Cobbs NGM_NAME, 508f8307e12SArchie Cobbs "name", 509f8307e12SArchie Cobbs &ng_generic_name_type, 510f8307e12SArchie Cobbs NULL 511f8307e12SArchie Cobbs }, 512f8307e12SArchie Cobbs { 513f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 514f8307e12SArchie Cobbs NGM_RMHOOK, 515f8307e12SArchie Cobbs "rmhook", 516f8307e12SArchie Cobbs &ng_generic_rmhook_type, 517f8307e12SArchie Cobbs NULL 518f8307e12SArchie Cobbs }, 519f8307e12SArchie Cobbs { 520f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 521f8307e12SArchie Cobbs NGM_NODEINFO, 522f8307e12SArchie Cobbs "nodeinfo", 523f8307e12SArchie Cobbs NULL, 524f8307e12SArchie Cobbs &ng_generic_nodeinfo_type 525f8307e12SArchie Cobbs }, 526f8307e12SArchie Cobbs { 527f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 528f8307e12SArchie Cobbs NGM_LISTHOOKS, 529f8307e12SArchie Cobbs "listhooks", 530f8307e12SArchie Cobbs NULL, 531f8307e12SArchie Cobbs &ng_generic_hooklist_type 532f8307e12SArchie Cobbs }, 533f8307e12SArchie Cobbs { 534f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 535f8307e12SArchie Cobbs NGM_LISTNAMES, 536f8307e12SArchie Cobbs "listnames", 537f8307e12SArchie Cobbs NULL, 538f8307e12SArchie Cobbs &ng_generic_listnodes_type /* same as NGM_LISTNODES */ 539f8307e12SArchie Cobbs }, 540f8307e12SArchie Cobbs { 541f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 542f8307e12SArchie Cobbs NGM_LISTNODES, 543f8307e12SArchie Cobbs "listnodes", 544f8307e12SArchie Cobbs NULL, 545f8307e12SArchie Cobbs &ng_generic_listnodes_type 546f8307e12SArchie Cobbs }, 547f8307e12SArchie Cobbs { 548f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 549f8307e12SArchie Cobbs NGM_LISTTYPES, 550f8307e12SArchie Cobbs "listtypes", 551f8307e12SArchie Cobbs NULL, 552f8307e12SArchie Cobbs &ng_generic_typeinfo_type 553f8307e12SArchie Cobbs }, 554f8307e12SArchie Cobbs { 555f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 5567095e097SPoul-Henning Kamp NGM_TEXT_CONFIG, 5577095e097SPoul-Henning Kamp "textconfig", 5587095e097SPoul-Henning Kamp NULL, 5597095e097SPoul-Henning Kamp &ng_parse_string_type 5607095e097SPoul-Henning Kamp }, 5617095e097SPoul-Henning Kamp { 5627095e097SPoul-Henning Kamp NGM_GENERIC_COOKIE, 563f8307e12SArchie Cobbs NGM_TEXT_STATUS, 564f8307e12SArchie Cobbs "textstatus", 565f8307e12SArchie Cobbs NULL, 566f8307e12SArchie Cobbs &ng_parse_string_type 567f8307e12SArchie Cobbs }, 568f8307e12SArchie Cobbs { 569f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 570f8307e12SArchie Cobbs NGM_ASCII2BINARY, 571f8307e12SArchie Cobbs "ascii2binary", 572f8307e12SArchie Cobbs &ng_parse_ng_mesg_type, 573f8307e12SArchie Cobbs &ng_parse_ng_mesg_type 574f8307e12SArchie Cobbs }, 575f8307e12SArchie Cobbs { 576f8307e12SArchie Cobbs NGM_GENERIC_COOKIE, 577f8307e12SArchie Cobbs NGM_BINARY2ASCII, 578f8307e12SArchie Cobbs "binary2ascii", 579f8307e12SArchie Cobbs &ng_parse_ng_mesg_type, 580f8307e12SArchie Cobbs &ng_parse_ng_mesg_type 581f8307e12SArchie Cobbs }, 582f8307e12SArchie Cobbs { 0 } 583f8307e12SArchie Cobbs }; 584f8307e12SArchie Cobbs 585f8307e12SArchie Cobbs /************************************************************************ 5864cf49a43SJulian Elischer Node routines 5874cf49a43SJulian Elischer ************************************************************************/ 5884cf49a43SJulian Elischer 5894cf49a43SJulian Elischer /* 5904cf49a43SJulian Elischer * Instantiate a node of the requested type 5914cf49a43SJulian Elischer */ 5924cf49a43SJulian Elischer int 5934cf49a43SJulian Elischer ng_make_node(const char *typename, node_p *nodepp) 5944cf49a43SJulian Elischer { 5954cf49a43SJulian Elischer struct ng_type *type; 596069154d5SJulian Elischer int error; 5974cf49a43SJulian Elischer 5984cf49a43SJulian Elischer /* Check that the type makes sense */ 5994cf49a43SJulian Elischer if (typename == NULL) { 6006b795970SJulian Elischer TRAP_ERROR(); 6014cf49a43SJulian Elischer return (EINVAL); 6024cf49a43SJulian Elischer } 6034cf49a43SJulian Elischer 6047610f574SGleb Smirnoff /* Locate the node type. If we fail we return. Do not try to load 6057610f574SGleb Smirnoff * module. 6067610f574SGleb Smirnoff */ 6074cf49a43SJulian Elischer if ((type = ng_findtype(typename)) == NULL) 6084cf49a43SJulian Elischer return (ENXIO); 6094cf49a43SJulian Elischer 610069154d5SJulian Elischer /* 611069154d5SJulian Elischer * If we have a constructor, then make the node and 612069154d5SJulian Elischer * call the constructor to do type specific initialisation. 613069154d5SJulian Elischer */ 614069154d5SJulian Elischer if (type->constructor != NULL) { 615069154d5SJulian Elischer if ((error = ng_make_node_common(type, nodepp)) == 0) { 6165633ca71SGleb Smirnoff if ((error = ((*type->constructor)(*nodepp))) != 0) { 61730400f03SJulian Elischer NG_NODE_UNREF(*nodepp); 618069154d5SJulian Elischer } 619069154d5SJulian Elischer } 620069154d5SJulian Elischer } else { 621069154d5SJulian Elischer /* 622069154d5SJulian Elischer * Node has no constructor. We cannot ask for one 62364efc707SRobert Watson * to be made. It must be brought into existence by 624954c4772SJulian Elischer * some external agency. The external agency should 625069154d5SJulian Elischer * call ng_make_node_common() directly to get the 626069154d5SJulian Elischer * netgraph part initialised. 627069154d5SJulian Elischer */ 6286b795970SJulian Elischer TRAP_ERROR(); 629069154d5SJulian Elischer error = EINVAL; 630069154d5SJulian Elischer } 631069154d5SJulian Elischer return (error); 6324cf49a43SJulian Elischer } 6334cf49a43SJulian Elischer 6344cf49a43SJulian Elischer /* 635069154d5SJulian Elischer * Generic node creation. Called by node initialisation for externally 636069154d5SJulian Elischer * instantiated nodes (e.g. hardware, sockets, etc ). 6374cf49a43SJulian Elischer * The returned node has a reference count of 1. 6384cf49a43SJulian Elischer */ 6394cf49a43SJulian Elischer int 6404cf49a43SJulian Elischer ng_make_node_common(struct ng_type *type, node_p *nodepp) 6414cf49a43SJulian Elischer { 6424cf49a43SJulian Elischer node_p node; 6434cf49a43SJulian Elischer 6444cf49a43SJulian Elischer /* Require the node type to have been already installed */ 6454cf49a43SJulian Elischer if (ng_findtype(type->name) == NULL) { 6466b795970SJulian Elischer TRAP_ERROR(); 6474cf49a43SJulian Elischer return (EINVAL); 6484cf49a43SJulian Elischer } 6494cf49a43SJulian Elischer 6504cf49a43SJulian Elischer /* Make a node and try attach it to the type */ 65130400f03SJulian Elischer NG_ALLOC_NODE(node); 6524cf49a43SJulian Elischer if (node == NULL) { 6536b795970SJulian Elischer TRAP_ERROR(); 6544cf49a43SJulian Elischer return (ENOMEM); 6554cf49a43SJulian Elischer } 65630400f03SJulian Elischer node->nd_type = type; 657bc29160dSMarko Zec #ifdef VIMAGE 658bc29160dSMarko Zec node->nd_vnet = curvnet; 659bc29160dSMarko Zec #endif 66030400f03SJulian Elischer NG_NODE_REF(node); /* note reference */ 6614cf49a43SJulian Elischer type->refs++; 6624cf49a43SJulian Elischer 6632c8dda8dSWojciech A. Koszek NG_QUEUE_LOCK_INIT(&node->nd_input_queue); 6649852972bSAlexander Motin STAILQ_INIT(&node->nd_input_queue.queue); 66530400f03SJulian Elischer node->nd_input_queue.q_flags = 0; 6664cf49a43SJulian Elischer 6674cf49a43SJulian Elischer /* Initialize hook list for new node */ 66830400f03SJulian Elischer LIST_INIT(&node->nd_hooks); 6694cf49a43SJulian Elischer 670687adb70SGleb Smirnoff /* Get an ID and put us in the hash chain. */ 671c4282b74SGleb Smirnoff IDHASH_WLOCK(); 67230400f03SJulian Elischer for (;;) { /* wrap protection, even if silly */ 673069154d5SJulian Elischer node_p node2 = NULL; 674ac957cd2SJulian Elischer node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */ 6750f150d04SJulian Elischer 67630400f03SJulian Elischer /* Is there a problem with the new number? */ 6770f150d04SJulian Elischer NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */ 6780f150d04SJulian Elischer if ((node->nd_ID != 0) && (node2 == NULL)) { 67930400f03SJulian Elischer break; 680069154d5SJulian Elischer } 68130400f03SJulian Elischer } 682687adb70SGleb Smirnoff V_ng_nodes++; 683687adb70SGleb Smirnoff if (V_ng_nodes * 2 > V_ng_ID_hmask) 684687adb70SGleb Smirnoff ng_ID_rehash(); 6854bd1b557SGleb Smirnoff LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], node, 6864bd1b557SGleb Smirnoff nd_idnodes); 687c4282b74SGleb Smirnoff IDHASH_WUNLOCK(); 688dc90cad9SJulian Elischer 6894cf49a43SJulian Elischer /* Done */ 6904cf49a43SJulian Elischer *nodepp = node; 6914cf49a43SJulian Elischer return (0); 6924cf49a43SJulian Elischer } 6934cf49a43SJulian Elischer 6944cf49a43SJulian Elischer /* 6954cf49a43SJulian Elischer * Forceably start the shutdown process on a node. Either call 69664efc707SRobert Watson * its shutdown method, or do the default shutdown if there is 6974cf49a43SJulian Elischer * no type-specific method. 6984cf49a43SJulian Elischer * 69964efc707SRobert Watson * We can only be called from a shutdown message, so we know we have 7003e4084c8SJulian Elischer * a writer lock, and therefore exclusive access. It also means 7013e4084c8SJulian Elischer * that we should not be on the work queue, but we check anyhow. 702069154d5SJulian Elischer * 703069154d5SJulian Elischer * Persistent node types must have a type-specific method which 70464efc707SRobert Watson * allocates a new node in which case, this one is irretrievably going away, 7053e4084c8SJulian Elischer * or cleans up anything it needs, and just makes the node valid again, 7063e4084c8SJulian Elischer * in which case we allow the node to survive. 7073e4084c8SJulian Elischer * 70864efc707SRobert Watson * XXX We need to think of how to tell a persistent node that we 7093e4084c8SJulian Elischer * REALLY need to go away because the hardware has gone or we 7103e4084c8SJulian Elischer * are rebooting.... etc. 7114cf49a43SJulian Elischer */ 7124cf49a43SJulian Elischer void 7131acb27c6SJulian Elischer ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3) 7144cf49a43SJulian Elischer { 7153e4084c8SJulian Elischer hook_p hook; 7163e4084c8SJulian Elischer 7174cf49a43SJulian Elischer /* Check if it's already shutting down */ 718be4252b3SJulian Elischer if ((node->nd_flags & NGF_CLOSING) != 0) 7194cf49a43SJulian Elischer return; 7204cf49a43SJulian Elischer 7211acb27c6SJulian Elischer if (node == &ng_deadnode) { 7221acb27c6SJulian Elischer printf ("shutdown called on deadnode\n"); 7231acb27c6SJulian Elischer return; 7241acb27c6SJulian Elischer } 7251acb27c6SJulian Elischer 7264cf49a43SJulian Elischer /* Add an extra reference so it doesn't go away during this */ 72730400f03SJulian Elischer NG_NODE_REF(node); 7284cf49a43SJulian Elischer 72930400f03SJulian Elischer /* 73030400f03SJulian Elischer * Mark it invalid so any newcomers know not to try use it 73130400f03SJulian Elischer * Also add our own mark so we can't recurse 732be4252b3SJulian Elischer * note that NGF_INVALID does not do this as it's also set during 73330400f03SJulian Elischer * creation 73430400f03SJulian Elischer */ 735be4252b3SJulian Elischer node->nd_flags |= NGF_INVALID|NGF_CLOSING; 7364cf49a43SJulian Elischer 737991fc65aSJulian Elischer /* If node has its pre-shutdown method, then call it first*/ 738991fc65aSJulian Elischer if (node->nd_type && node->nd_type->close) 739991fc65aSJulian Elischer (*node->nd_type->close)(node); 740991fc65aSJulian Elischer 7413e4084c8SJulian Elischer /* Notify all remaining connected nodes to disconnect */ 7423e4084c8SJulian Elischer while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL) 7433e4084c8SJulian Elischer ng_destroy_hook(hook); 74430400f03SJulian Elischer 745069154d5SJulian Elischer /* 746069154d5SJulian Elischer * Drain the input queue forceably. 74730400f03SJulian Elischer * it has no hooks so what's it going to do, bleed on someone? 74830400f03SJulian Elischer * Theoretically we came here from a queue entry that was added 74930400f03SJulian Elischer * Just before the queue was closed, so it should be empty anyway. 750b57a7965SJulian Elischer * Also removes us from worklist if needed. 751069154d5SJulian Elischer */ 7529852972bSAlexander Motin ng_flush_input_queue(node); 753069154d5SJulian Elischer 754069154d5SJulian Elischer /* Ask the type if it has anything to do in this case */ 75530400f03SJulian Elischer if (node->nd_type && node->nd_type->shutdown) { 75630400f03SJulian Elischer (*node->nd_type->shutdown)(node); 75730400f03SJulian Elischer if (NG_NODE_IS_VALID(node)) { 75830400f03SJulian Elischer /* 75930400f03SJulian Elischer * Well, blow me down if the node code hasn't declared 76030400f03SJulian Elischer * that it doesn't want to die. 76130400f03SJulian Elischer * Presumably it is a persistant node. 7621acb27c6SJulian Elischer * If we REALLY want it to go away, 7631acb27c6SJulian Elischer * e.g. hardware going away, 764be4252b3SJulian Elischer * Our caller should set NGF_REALLY_DIE in nd_flags. 76530400f03SJulian Elischer */ 766be4252b3SJulian Elischer node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING); 7671acb27c6SJulian Elischer NG_NODE_UNREF(node); /* Assume they still have theirs */ 76830400f03SJulian Elischer return; 7694cf49a43SJulian Elischer } 7701acb27c6SJulian Elischer } else { /* do the default thing */ 7711acb27c6SJulian Elischer NG_NODE_UNREF(node); 7721acb27c6SJulian Elischer } 7734cf49a43SJulian Elischer 77430400f03SJulian Elischer ng_unname(node); /* basically a NOP these days */ 77530400f03SJulian Elischer 77630400f03SJulian Elischer /* 77730400f03SJulian Elischer * Remove extra reference, possibly the last 77830400f03SJulian Elischer * Possible other holders of references may include 77930400f03SJulian Elischer * timeout callouts, but theoretically the node's supposed to 78030400f03SJulian Elischer * have cancelled them. Possibly hardware dependencies may 78130400f03SJulian Elischer * force a driver to 'linger' with a reference. 78230400f03SJulian Elischer */ 78330400f03SJulian Elischer NG_NODE_UNREF(node); 7844cf49a43SJulian Elischer } 7854cf49a43SJulian Elischer 7865951069aSJulian Elischer /* 7875951069aSJulian Elischer * Remove a reference to the node, possibly the last. 7885951069aSJulian Elischer * deadnode always acts as it it were the last. 7895951069aSJulian Elischer */ 7903fbdf774SGleb Smirnoff void 79130400f03SJulian Elischer ng_unref_node(node_p node) 7924cf49a43SJulian Elischer { 7936b795970SJulian Elischer 7943fbdf774SGleb Smirnoff if (node == &ng_deadnode) 7953fbdf774SGleb Smirnoff return; 7966b795970SJulian Elischer 797719fb725SCraig Rodrigues CURVNET_SET(node->nd_vnet); 798719fb725SCraig Rodrigues 7993fbdf774SGleb Smirnoff if (refcount_release(&node->nd_refs)) { /* we were the last */ 800069154d5SJulian Elischer 80130400f03SJulian Elischer node->nd_type->refs--; /* XXX maybe should get types lock? */ 802c4282b74SGleb Smirnoff NAMEHASH_WLOCK(); 803687adb70SGleb Smirnoff if (NG_NODE_HAS_NAME(node)) { 804687adb70SGleb Smirnoff V_ng_named_nodes--; 80530400f03SJulian Elischer LIST_REMOVE(node, nd_nodes); 806687adb70SGleb Smirnoff } 807c4282b74SGleb Smirnoff NAMEHASH_WUNLOCK(); 808069154d5SJulian Elischer 809c4282b74SGleb Smirnoff IDHASH_WLOCK(); 810687adb70SGleb Smirnoff V_ng_nodes--; 81130400f03SJulian Elischer LIST_REMOVE(node, nd_idnodes); 812c4282b74SGleb Smirnoff IDHASH_WUNLOCK(); 813069154d5SJulian Elischer 81412574a02SJulian Elischer mtx_destroy(&node->nd_input_queue.q_mtx); 815069154d5SJulian Elischer NG_FREE_NODE(node); 8164cf49a43SJulian Elischer } 817719fb725SCraig Rodrigues CURVNET_RESTORE(); 8184cf49a43SJulian Elischer } 8194cf49a43SJulian Elischer 8204cf49a43SJulian Elischer /************************************************************************ 821dc90cad9SJulian Elischer Node ID handling 822dc90cad9SJulian Elischer ************************************************************************/ 823dc90cad9SJulian Elischer static node_p 824069154d5SJulian Elischer ng_ID2noderef(ng_ID_t ID) 825dc90cad9SJulian Elischer { 82630400f03SJulian Elischer node_p node; 827687adb70SGleb Smirnoff 828c4282b74SGleb Smirnoff IDHASH_RLOCK(); 8290f150d04SJulian Elischer NG_IDHASH_FIND(ID, node); 83030400f03SJulian Elischer if (node) 83130400f03SJulian Elischer NG_NODE_REF(node); 832c4282b74SGleb Smirnoff IDHASH_RUNLOCK(); 83330400f03SJulian Elischer return(node); 834dc90cad9SJulian Elischer } 835dc90cad9SJulian Elischer 836dc90cad9SJulian Elischer ng_ID_t 837dc90cad9SJulian Elischer ng_node2ID(node_p node) 838dc90cad9SJulian Elischer { 83970de87f2SJulian Elischer return (node ? NG_NODE_ID(node) : 0); 840dc90cad9SJulian Elischer } 841dc90cad9SJulian Elischer 842dc90cad9SJulian Elischer /************************************************************************ 8434cf49a43SJulian Elischer Node name handling 8444cf49a43SJulian Elischer ************************************************************************/ 8454cf49a43SJulian Elischer 8464cf49a43SJulian Elischer /* 8474bd1b557SGleb Smirnoff * Assign a node a name. 8484cf49a43SJulian Elischer */ 8494cf49a43SJulian Elischer int 8504cf49a43SJulian Elischer ng_name_node(node_p node, const char *name) 8514cf49a43SJulian Elischer { 852687adb70SGleb Smirnoff uint32_t hash; 853069154d5SJulian Elischer node_p node2; 854687adb70SGleb Smirnoff int i; 8554cf49a43SJulian Elischer 8564cf49a43SJulian Elischer /* Check the name is valid */ 85787e2c66aSHartmut Brandt for (i = 0; i < NG_NODESIZ; i++) { 8584cf49a43SJulian Elischer if (name[i] == '\0' || name[i] == '.' || name[i] == ':') 8594cf49a43SJulian Elischer break; 8604cf49a43SJulian Elischer } 8614cf49a43SJulian Elischer if (i == 0 || name[i] != '\0') { 8626b795970SJulian Elischer TRAP_ERROR(); 8634cf49a43SJulian Elischer return (EINVAL); 8644cf49a43SJulian Elischer } 865dc90cad9SJulian Elischer if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ 8666b795970SJulian Elischer TRAP_ERROR(); 8674cf49a43SJulian Elischer return (EINVAL); 8684cf49a43SJulian Elischer } 8694cf49a43SJulian Elischer 870687adb70SGleb Smirnoff NAMEHASH_WLOCK(); 871687adb70SGleb Smirnoff if (V_ng_named_nodes * 2 > V_ng_name_hmask) 872687adb70SGleb Smirnoff ng_name_rehash(); 873687adb70SGleb Smirnoff 874687adb70SGleb Smirnoff hash = hash32_str(name, HASHINIT) & V_ng_name_hmask; 875687adb70SGleb Smirnoff /* Check the name isn't already being used. */ 876687adb70SGleb Smirnoff LIST_FOREACH(node2, &V_ng_name_hash[hash], nd_nodes) 877687adb70SGleb Smirnoff if (NG_NODE_IS_VALID(node2) && 878687adb70SGleb Smirnoff (strcmp(NG_NODE_NAME(node2), name) == 0)) { 879687adb70SGleb Smirnoff NAMEHASH_WUNLOCK(); 8804cf49a43SJulian Elischer return (EADDRINUSE); 8814cf49a43SJulian Elischer } 8824cf49a43SJulian Elischer 883687adb70SGleb Smirnoff if (NG_NODE_HAS_NAME(node)) 884cfea3f85SAlexander Motin LIST_REMOVE(node, nd_nodes); 885687adb70SGleb Smirnoff else 886687adb70SGleb Smirnoff V_ng_named_nodes++; 887687adb70SGleb Smirnoff /* Copy it. */ 888687adb70SGleb Smirnoff strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ); 889687adb70SGleb Smirnoff /* Update name hash. */ 890603724d3SBjoern A. Zeeb LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes); 891c4282b74SGleb Smirnoff NAMEHASH_WUNLOCK(); 892cfea3f85SAlexander Motin 8934cf49a43SJulian Elischer return (0); 8944cf49a43SJulian Elischer } 8954cf49a43SJulian Elischer 8964cf49a43SJulian Elischer /* 8974cf49a43SJulian Elischer * Find a node by absolute name. The name should NOT end with ':' 8984cf49a43SJulian Elischer * The name "." means "this node" and "[xxx]" means "the node 8994cf49a43SJulian Elischer * with ID (ie, at address) xxx". 9004cf49a43SJulian Elischer * 9014cf49a43SJulian Elischer * Returns the node if found, else NULL. 902069154d5SJulian Elischer * Eventually should add something faster than a sequential search. 903e1e8f51bSRobert Watson * Note it acquires a reference on the node so you can be sure it's still 904e1e8f51bSRobert Watson * there. 9054cf49a43SJulian Elischer */ 9064cf49a43SJulian Elischer node_p 907069154d5SJulian Elischer ng_name2noderef(node_p here, const char *name) 9084cf49a43SJulian Elischer { 909dc90cad9SJulian Elischer node_p node; 910dc90cad9SJulian Elischer ng_ID_t temp; 911cfea3f85SAlexander Motin int hash; 9124cf49a43SJulian Elischer 9134cf49a43SJulian Elischer /* "." means "this node" */ 914069154d5SJulian Elischer if (strcmp(name, ".") == 0) { 91530400f03SJulian Elischer NG_NODE_REF(here); 916069154d5SJulian Elischer return(here); 917069154d5SJulian Elischer } 9184cf49a43SJulian Elischer 9194cf49a43SJulian Elischer /* Check for name-by-ID */ 920dc90cad9SJulian Elischer if ((temp = ng_decodeidname(name)) != 0) { 921069154d5SJulian Elischer return (ng_ID2noderef(temp)); 9224cf49a43SJulian Elischer } 9234cf49a43SJulian Elischer 924687adb70SGleb Smirnoff /* Find node by name. */ 925687adb70SGleb Smirnoff hash = hash32_str(name, HASHINIT) & V_ng_name_hmask; 926c4282b74SGleb Smirnoff NAMEHASH_RLOCK(); 927c4282b74SGleb Smirnoff LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes) 928cfea3f85SAlexander Motin if (NG_NODE_IS_VALID(node) && 929cfea3f85SAlexander Motin (strcmp(NG_NODE_NAME(node), name) == 0)) { 930c4282b74SGleb Smirnoff NG_NODE_REF(node); 9314cf49a43SJulian Elischer break; 9324cf49a43SJulian Elischer } 933c4282b74SGleb Smirnoff NAMEHASH_RUNLOCK(); 934c4282b74SGleb Smirnoff 9354cf49a43SJulian Elischer return (node); 9364cf49a43SJulian Elischer } 9374cf49a43SJulian Elischer 9384cf49a43SJulian Elischer /* 9399d5abbddSJens Schweikhardt * Decode an ID name, eg. "[f03034de]". Returns 0 if the 940dc90cad9SJulian Elischer * string is not valid, otherwise returns the value. 9414cf49a43SJulian Elischer */ 942dc90cad9SJulian Elischer static ng_ID_t 9434cf49a43SJulian Elischer ng_decodeidname(const char *name) 9444cf49a43SJulian Elischer { 9452b70adcbSArchie Cobbs const int len = strlen(name); 94625792ef3SArchie Cobbs char *eptr; 9472b70adcbSArchie Cobbs u_long val; 9484cf49a43SJulian Elischer 9492b70adcbSArchie Cobbs /* Check for proper length, brackets, no leading junk */ 9504bd1b557SGleb Smirnoff if ((len < 3) || (name[0] != '[') || (name[len - 1] != ']') || 9514bd1b557SGleb Smirnoff (!isxdigit(name[1]))) 95270de87f2SJulian Elischer return ((ng_ID_t)0); 9534cf49a43SJulian Elischer 9542b70adcbSArchie Cobbs /* Decode number */ 9552b70adcbSArchie Cobbs val = strtoul(name + 1, &eptr, 16); 9564bd1b557SGleb Smirnoff if ((eptr - name != len - 1) || (val == ULONG_MAX) || (val == 0)) 95712f035e0SJulian Elischer return ((ng_ID_t)0); 9584bd1b557SGleb Smirnoff 9594bd1b557SGleb Smirnoff return ((ng_ID_t)val); 9604cf49a43SJulian Elischer } 9614cf49a43SJulian Elischer 9624cf49a43SJulian Elischer /* 9634cf49a43SJulian Elischer * Remove a name from a node. This should only be called 9644cf49a43SJulian Elischer * when shutting down and removing the node. 9654cf49a43SJulian Elischer */ 9664cf49a43SJulian Elischer void 9674cf49a43SJulian Elischer ng_unname(node_p node) 9684cf49a43SJulian Elischer { 9694cf49a43SJulian Elischer } 9704cf49a43SJulian Elischer 971687adb70SGleb Smirnoff /* 972687adb70SGleb Smirnoff * Allocate a bigger name hash. 973687adb70SGleb Smirnoff */ 974687adb70SGleb Smirnoff static void 975687adb70SGleb Smirnoff ng_name_rehash() 976687adb70SGleb Smirnoff { 977687adb70SGleb Smirnoff struct nodehash *new; 978687adb70SGleb Smirnoff uint32_t hash; 979687adb70SGleb Smirnoff u_long hmask; 980687adb70SGleb Smirnoff node_p node, node2; 981687adb70SGleb Smirnoff int i; 982687adb70SGleb Smirnoff 983687adb70SGleb Smirnoff new = hashinit_flags((V_ng_name_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask, 984687adb70SGleb Smirnoff HASH_NOWAIT); 985687adb70SGleb Smirnoff if (new == NULL) 986687adb70SGleb Smirnoff return; 987687adb70SGleb Smirnoff 988687adb70SGleb Smirnoff for (i = 0; i <= V_ng_name_hmask; i++) 989687adb70SGleb Smirnoff LIST_FOREACH_SAFE(node, &V_ng_name_hash[i], nd_nodes, node2) { 990687adb70SGleb Smirnoff #ifdef INVARIANTS 991687adb70SGleb Smirnoff LIST_REMOVE(node, nd_nodes); 992687adb70SGleb Smirnoff #endif 993687adb70SGleb Smirnoff hash = hash32_str(NG_NODE_NAME(node), HASHINIT) & hmask; 994687adb70SGleb Smirnoff LIST_INSERT_HEAD(&new[hash], node, nd_nodes); 995687adb70SGleb Smirnoff } 996687adb70SGleb Smirnoff 997687adb70SGleb Smirnoff hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask); 998687adb70SGleb Smirnoff V_ng_name_hash = new; 999687adb70SGleb Smirnoff V_ng_name_hmask = hmask; 1000687adb70SGleb Smirnoff } 1001687adb70SGleb Smirnoff 1002687adb70SGleb Smirnoff /* 1003687adb70SGleb Smirnoff * Allocate a bigger ID hash. 1004687adb70SGleb Smirnoff */ 1005687adb70SGleb Smirnoff static void 1006687adb70SGleb Smirnoff ng_ID_rehash() 1007687adb70SGleb Smirnoff { 1008687adb70SGleb Smirnoff struct nodehash *new; 1009687adb70SGleb Smirnoff uint32_t hash; 1010687adb70SGleb Smirnoff u_long hmask; 1011687adb70SGleb Smirnoff node_p node, node2; 1012687adb70SGleb Smirnoff int i; 1013687adb70SGleb Smirnoff 1014687adb70SGleb Smirnoff new = hashinit_flags((V_ng_ID_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask, 1015687adb70SGleb Smirnoff HASH_NOWAIT); 1016687adb70SGleb Smirnoff if (new == NULL) 1017687adb70SGleb Smirnoff return; 1018687adb70SGleb Smirnoff 1019687adb70SGleb Smirnoff for (i = 0; i <= V_ng_ID_hmask; i++) 1020687adb70SGleb Smirnoff LIST_FOREACH_SAFE(node, &V_ng_ID_hash[i], nd_idnodes, node2) { 1021687adb70SGleb Smirnoff #ifdef INVARIANTS 1022687adb70SGleb Smirnoff LIST_REMOVE(node, nd_idnodes); 1023687adb70SGleb Smirnoff #endif 1024687adb70SGleb Smirnoff hash = (node->nd_ID % (hmask + 1)); 1025687adb70SGleb Smirnoff LIST_INSERT_HEAD(&new[hash], node, nd_idnodes); 1026687adb70SGleb Smirnoff } 1027687adb70SGleb Smirnoff 1028687adb70SGleb Smirnoff hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_name_hmask); 1029687adb70SGleb Smirnoff V_ng_ID_hash = new; 1030687adb70SGleb Smirnoff V_ng_ID_hmask = hmask; 1031687adb70SGleb Smirnoff } 1032687adb70SGleb Smirnoff 10334cf49a43SJulian Elischer /************************************************************************ 10344cf49a43SJulian Elischer Hook routines 10354cf49a43SJulian Elischer Names are not optional. Hooks are always connected, except for a 10363e4084c8SJulian Elischer brief moment within these routines. On invalidation or during creation 10373e4084c8SJulian Elischer they are connected to the 'dead' hook. 10384cf49a43SJulian Elischer ************************************************************************/ 10394cf49a43SJulian Elischer 10404cf49a43SJulian Elischer /* 10414cf49a43SJulian Elischer * Remove a hook reference 10424cf49a43SJulian Elischer */ 104330400f03SJulian Elischer void 10444cf49a43SJulian Elischer ng_unref_hook(hook_p hook) 10454cf49a43SJulian Elischer { 10466b795970SJulian Elischer 10473fbdf774SGleb Smirnoff if (hook == &ng_deadhook) 10486b795970SJulian Elischer return; 1049018fe3d1SAlexander Motin 10503fbdf774SGleb Smirnoff if (refcount_release(&hook->hk_refs)) { /* we were the last */ 1051f573da1aSAlexander Motin if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */ 10526b795970SJulian Elischer _NG_NODE_UNREF((_NG_HOOK_NODE(hook))); 1053069154d5SJulian Elischer NG_FREE_HOOK(hook); 1054069154d5SJulian Elischer } 10554cf49a43SJulian Elischer } 10564cf49a43SJulian Elischer 10574cf49a43SJulian Elischer /* 10584cf49a43SJulian Elischer * Add an unconnected hook to a node. Only used internally. 10593e4084c8SJulian Elischer * Assumes node is locked. (XXX not yet true ) 10604cf49a43SJulian Elischer */ 10614cf49a43SJulian Elischer static int 10624cf49a43SJulian Elischer ng_add_hook(node_p node, const char *name, hook_p *hookp) 10634cf49a43SJulian Elischer { 10644cf49a43SJulian Elischer hook_p hook; 10654cf49a43SJulian Elischer int error = 0; 10664cf49a43SJulian Elischer 10674cf49a43SJulian Elischer /* Check that the given name is good */ 10684cf49a43SJulian Elischer if (name == NULL) { 10696b795970SJulian Elischer TRAP_ERROR(); 10704cf49a43SJulian Elischer return (EINVAL); 10714cf49a43SJulian Elischer } 1072899e9c4eSArchie Cobbs if (ng_findhook(node, name) != NULL) { 10736b795970SJulian Elischer TRAP_ERROR(); 10744cf49a43SJulian Elischer return (EEXIST); 10754cf49a43SJulian Elischer } 10764cf49a43SJulian Elischer 10774cf49a43SJulian Elischer /* Allocate the hook and link it up */ 107830400f03SJulian Elischer NG_ALLOC_HOOK(hook); 10794cf49a43SJulian Elischer if (hook == NULL) { 10806b795970SJulian Elischer TRAP_ERROR(); 10814cf49a43SJulian Elischer return (ENOMEM); 10824cf49a43SJulian Elischer } 10833e4084c8SJulian Elischer hook->hk_refs = 1; /* add a reference for us to return */ 108430400f03SJulian Elischer hook->hk_flags = HK_INVALID; 10853e4084c8SJulian Elischer hook->hk_peer = &ng_deadhook; /* start off this way */ 108630400f03SJulian Elischer hook->hk_node = node; 108730400f03SJulian Elischer NG_NODE_REF(node); /* each hook counts as a reference */ 10884cf49a43SJulian Elischer 10893e4084c8SJulian Elischer /* Set hook name */ 109087e2c66aSHartmut Brandt strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ); 10913e4084c8SJulian Elischer 10923e4084c8SJulian Elischer /* 10933e4084c8SJulian Elischer * Check if the node type code has something to say about it 10943e4084c8SJulian Elischer * If it fails, the unref of the hook will also unref the node. 10953e4084c8SJulian Elischer */ 1096954c4772SJulian Elischer if (node->nd_type->newhook != NULL) { 1097954c4772SJulian Elischer if ((error = (*node->nd_type->newhook)(node, hook, name))) { 109830400f03SJulian Elischer NG_HOOK_UNREF(hook); /* this frees the hook */ 1099069154d5SJulian Elischer return (error); 1100069154d5SJulian Elischer } 1101954c4772SJulian Elischer } 11024cf49a43SJulian Elischer /* 11034cf49a43SJulian Elischer * The 'type' agrees so far, so go ahead and link it in. 11044cf49a43SJulian Elischer * We'll ask again later when we actually connect the hooks. 11054cf49a43SJulian Elischer */ 110630400f03SJulian Elischer LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 110730400f03SJulian Elischer node->nd_numhooks++; 11083e4084c8SJulian Elischer NG_HOOK_REF(hook); /* one for the node */ 11094cf49a43SJulian Elischer 11104cf49a43SJulian Elischer if (hookp) 11114cf49a43SJulian Elischer *hookp = hook; 11123e4084c8SJulian Elischer return (0); 11134cf49a43SJulian Elischer } 11144cf49a43SJulian Elischer 11154cf49a43SJulian Elischer /* 1116899e9c4eSArchie Cobbs * Find a hook 1117899e9c4eSArchie Cobbs * 1118899e9c4eSArchie Cobbs * Node types may supply their own optimized routines for finding 1119899e9c4eSArchie Cobbs * hooks. If none is supplied, we just do a linear search. 11203e4084c8SJulian Elischer * XXX Possibly we should add a reference to the hook? 1121899e9c4eSArchie Cobbs */ 1122899e9c4eSArchie Cobbs hook_p 1123899e9c4eSArchie Cobbs ng_findhook(node_p node, const char *name) 1124899e9c4eSArchie Cobbs { 1125899e9c4eSArchie Cobbs hook_p hook; 1126899e9c4eSArchie Cobbs 112730400f03SJulian Elischer if (node->nd_type->findhook != NULL) 112830400f03SJulian Elischer return (*node->nd_type->findhook)(node, name); 112930400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 11304bd1b557SGleb Smirnoff if (NG_HOOK_IS_VALID(hook) && 11314bd1b557SGleb Smirnoff (strcmp(NG_HOOK_NAME(hook), name) == 0)) 1132899e9c4eSArchie Cobbs return (hook); 1133899e9c4eSArchie Cobbs } 1134899e9c4eSArchie Cobbs return (NULL); 1135899e9c4eSArchie Cobbs } 1136899e9c4eSArchie Cobbs 1137899e9c4eSArchie Cobbs /* 11384cf49a43SJulian Elischer * Destroy a hook 11394cf49a43SJulian Elischer * 11404cf49a43SJulian Elischer * As hooks are always attached, this really destroys two hooks. 11414cf49a43SJulian Elischer * The one given, and the one attached to it. Disconnect the hooks 11423e4084c8SJulian Elischer * from each other first. We reconnect the peer hook to the 'dead' 11433e4084c8SJulian Elischer * hook so that it can still exist after we depart. We then 11443e4084c8SJulian Elischer * send the peer its own destroy message. This ensures that we only 11453e4084c8SJulian Elischer * interact with the peer's structures when it is locked processing that 11463e4084c8SJulian Elischer * message. We hold a reference to the peer hook so we are guaranteed that 11473e4084c8SJulian Elischer * the peer hook and node are still going to exist until 11483e4084c8SJulian Elischer * we are finished there as the hook holds a ref on the node. 11493e4084c8SJulian Elischer * We run this same code again on the peer hook, but that time it is already 11503e4084c8SJulian Elischer * attached to the 'dead' hook. 11516b795970SJulian Elischer * 11526b795970SJulian Elischer * This routine is called at all stages of hook creation 11536b795970SJulian Elischer * on error detection and must be able to handle any such stage. 11544cf49a43SJulian Elischer */ 11554cf49a43SJulian Elischer void 11564cf49a43SJulian Elischer ng_destroy_hook(hook_p hook) 11574cf49a43SJulian Elischer { 1158ac5dd141SGleb Smirnoff hook_p peer; 1159ac5dd141SGleb Smirnoff node_p node; 11604cf49a43SJulian Elischer 11616b795970SJulian Elischer if (hook == &ng_deadhook) { /* better safe than sorry */ 11626b795970SJulian Elischer printf("ng_destroy_hook called on deadhook\n"); 11636b795970SJulian Elischer return; 11646b795970SJulian Elischer } 1165ac5dd141SGleb Smirnoff 1166ac5dd141SGleb Smirnoff /* 1167ac5dd141SGleb Smirnoff * Protect divorce process with mutex, to avoid races on 1168ac5dd141SGleb Smirnoff * simultaneous disconnect. 1169ac5dd141SGleb Smirnoff */ 1170*d2fd0788SAlexander V. Chernikov TOPOLOGY_WLOCK(); 1171ac5dd141SGleb Smirnoff 1172ac5dd141SGleb Smirnoff hook->hk_flags |= HK_INVALID; 1173ac5dd141SGleb Smirnoff 1174ac5dd141SGleb Smirnoff peer = NG_HOOK_PEER(hook); 1175ac5dd141SGleb Smirnoff node = NG_HOOK_NODE(hook); 1176ac5dd141SGleb Smirnoff 11773e4084c8SJulian Elischer if (peer && (peer != &ng_deadhook)) { 11783e4084c8SJulian Elischer /* 11793e4084c8SJulian Elischer * Set the peer to point to ng_deadhook 11803e4084c8SJulian Elischer * from this moment on we are effectively independent it. 11813e4084c8SJulian Elischer * send it an rmhook message of it's own. 11823e4084c8SJulian Elischer */ 11833e4084c8SJulian Elischer peer->hk_peer = &ng_deadhook; /* They no longer know us */ 11843e4084c8SJulian Elischer hook->hk_peer = &ng_deadhook; /* Nor us, them */ 11856b795970SJulian Elischer if (NG_HOOK_NODE(peer) == &ng_deadnode) { 11866b795970SJulian Elischer /* 11876b795970SJulian Elischer * If it's already divorced from a node, 11886b795970SJulian Elischer * just free it. 11896b795970SJulian Elischer */ 1190*d2fd0788SAlexander V. Chernikov TOPOLOGY_WUNLOCK(); 11916b795970SJulian Elischer } else { 1192*d2fd0788SAlexander V. Chernikov TOPOLOGY_WUNLOCK(); 11936b795970SJulian Elischer ng_rmhook_self(peer); /* Send it a surprise */ 11946b795970SJulian Elischer } 119552fa3556SJulian Elischer NG_HOOK_UNREF(peer); /* account for peer link */ 119652fa3556SJulian Elischer NG_HOOK_UNREF(hook); /* account for peer link */ 1197ac5dd141SGleb Smirnoff } else 1198*d2fd0788SAlexander V. Chernikov TOPOLOGY_WUNLOCK(); 1199ac5dd141SGleb Smirnoff 1200*d2fd0788SAlexander V. Chernikov TOPOLOGY_NOTOWNED(); 12014cf49a43SJulian Elischer 12024cf49a43SJulian Elischer /* 12034cf49a43SJulian Elischer * Remove the hook from the node's list to avoid possible recursion 12044cf49a43SJulian Elischer * in case the disconnection results in node shutdown. 12054cf49a43SJulian Elischer */ 12066b795970SJulian Elischer if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */ 12076b795970SJulian Elischer return; 12086b795970SJulian Elischer } 120930400f03SJulian Elischer LIST_REMOVE(hook, hk_hooks); 121030400f03SJulian Elischer node->nd_numhooks--; 121130400f03SJulian Elischer if (node->nd_type->disconnect) { 12124cf49a43SJulian Elischer /* 12136b795970SJulian Elischer * The type handler may elect to destroy the node so don't 121464efc707SRobert Watson * trust its existence after this point. (except 12156b795970SJulian Elischer * that we still hold a reference on it. (which we 12166b795970SJulian Elischer * inherrited from the hook we are destroying) 12174cf49a43SJulian Elischer */ 121830400f03SJulian Elischer (*node->nd_type->disconnect) (hook); 12194cf49a43SJulian Elischer } 12206b795970SJulian Elischer 12216b795970SJulian Elischer /* 12226b795970SJulian Elischer * Note that because we will point to ng_deadnode, the original node 12236b795970SJulian Elischer * is not decremented automatically so we do that manually. 12246b795970SJulian Elischer */ 12256b795970SJulian Elischer _NG_HOOK_NODE(hook) = &ng_deadnode; 12266b795970SJulian Elischer NG_NODE_UNREF(node); /* We no longer point to it so adjust count */ 12276b795970SJulian Elischer NG_HOOK_UNREF(hook); /* Account for linkage (in list) to node */ 12284cf49a43SJulian Elischer } 12294cf49a43SJulian Elischer 12304cf49a43SJulian Elischer /* 12314cf49a43SJulian Elischer * Take two hooks on a node and merge the connection so that the given node 12324cf49a43SJulian Elischer * is effectively bypassed. 12334cf49a43SJulian Elischer */ 12344cf49a43SJulian Elischer int 12354cf49a43SJulian Elischer ng_bypass(hook_p hook1, hook_p hook2) 12364cf49a43SJulian Elischer { 123730400f03SJulian Elischer if (hook1->hk_node != hook2->hk_node) { 12386b795970SJulian Elischer TRAP_ERROR(); 12394cf49a43SJulian Elischer return (EINVAL); 124030400f03SJulian Elischer } 1241*d2fd0788SAlexander V. Chernikov TOPOLOGY_WLOCK(); 1242c3189b3fSGleb Smirnoff if (NG_HOOK_NOT_VALID(hook1) || NG_HOOK_NOT_VALID(hook2)) { 1243*d2fd0788SAlexander V. Chernikov TOPOLOGY_WUNLOCK(); 1244c3189b3fSGleb Smirnoff return (EINVAL); 1245c3189b3fSGleb Smirnoff } 124630400f03SJulian Elischer hook1->hk_peer->hk_peer = hook2->hk_peer; 124730400f03SJulian Elischer hook2->hk_peer->hk_peer = hook1->hk_peer; 12484cf49a43SJulian Elischer 12493e4084c8SJulian Elischer hook1->hk_peer = &ng_deadhook; 12503e4084c8SJulian Elischer hook2->hk_peer = &ng_deadhook; 1251*d2fd0788SAlexander V. Chernikov TOPOLOGY_WUNLOCK(); 12523e4084c8SJulian Elischer 1253cf3254aaSGleb Smirnoff NG_HOOK_UNREF(hook1); 1254cf3254aaSGleb Smirnoff NG_HOOK_UNREF(hook2); 1255cf3254aaSGleb Smirnoff 12564cf49a43SJulian Elischer /* XXX If we ever cache methods on hooks update them as well */ 12574cf49a43SJulian Elischer ng_destroy_hook(hook1); 12584cf49a43SJulian Elischer ng_destroy_hook(hook2); 12594cf49a43SJulian Elischer return (0); 12604cf49a43SJulian Elischer } 12614cf49a43SJulian Elischer 12624cf49a43SJulian Elischer /* 12634cf49a43SJulian Elischer * Install a new netgraph type 12644cf49a43SJulian Elischer */ 12654cf49a43SJulian Elischer int 12664cf49a43SJulian Elischer ng_newtype(struct ng_type *tp) 12674cf49a43SJulian Elischer { 12684cf49a43SJulian Elischer const size_t namelen = strlen(tp->name); 12694cf49a43SJulian Elischer 12704cf49a43SJulian Elischer /* Check version and type name fields */ 12714bd1b557SGleb Smirnoff if ((tp->version != NG_ABI_VERSION) || (namelen == 0) || 12724bd1b557SGleb Smirnoff (namelen >= NG_TYPESIZ)) { 12736b795970SJulian Elischer TRAP_ERROR(); 12748ed370fdSJulian Elischer if (tp->version != NG_ABI_VERSION) { 12754bd1b557SGleb Smirnoff printf("Netgraph: Node type rejected. ABI mismatch. " 12764bd1b557SGleb Smirnoff "Suggest recompile\n"); 12778ed370fdSJulian Elischer } 12784cf49a43SJulian Elischer return (EINVAL); 12794cf49a43SJulian Elischer } 12804cf49a43SJulian Elischer 12814cf49a43SJulian Elischer /* Check for name collision */ 12824cf49a43SJulian Elischer if (ng_findtype(tp->name) != NULL) { 12836b795970SJulian Elischer TRAP_ERROR(); 12844cf49a43SJulian Elischer return (EEXIST); 12854cf49a43SJulian Elischer } 12864cf49a43SJulian Elischer 1287069154d5SJulian Elischer /* Link in new type */ 1288c4282b74SGleb Smirnoff TYPELIST_WLOCK(); 1289069154d5SJulian Elischer LIST_INSERT_HEAD(&ng_typelist, tp, types); 1290c73b94a2SJulian Elischer tp->refs = 1; /* first ref is linked list */ 1291c4282b74SGleb Smirnoff TYPELIST_WUNLOCK(); 12924cf49a43SJulian Elischer return (0); 12934cf49a43SJulian Elischer } 12944cf49a43SJulian Elischer 12954cf49a43SJulian Elischer /* 1296c31b4a53SJulian Elischer * unlink a netgraph type 1297c31b4a53SJulian Elischer * If no examples exist 1298c31b4a53SJulian Elischer */ 1299c31b4a53SJulian Elischer int 1300c31b4a53SJulian Elischer ng_rmtype(struct ng_type *tp) 1301c31b4a53SJulian Elischer { 1302c31b4a53SJulian Elischer /* Check for name collision */ 1303c31b4a53SJulian Elischer if (tp->refs != 1) { 1304c31b4a53SJulian Elischer TRAP_ERROR(); 1305c31b4a53SJulian Elischer return (EBUSY); 1306c31b4a53SJulian Elischer } 1307c31b4a53SJulian Elischer 1308c31b4a53SJulian Elischer /* Unlink type */ 1309c4282b74SGleb Smirnoff TYPELIST_WLOCK(); 1310c31b4a53SJulian Elischer LIST_REMOVE(tp, types); 1311c4282b74SGleb Smirnoff TYPELIST_WUNLOCK(); 1312c31b4a53SJulian Elischer return (0); 1313c31b4a53SJulian Elischer } 1314c31b4a53SJulian Elischer 1315c31b4a53SJulian Elischer /* 13164cf49a43SJulian Elischer * Look for a type of the name given 13174cf49a43SJulian Elischer */ 13184cf49a43SJulian Elischer struct ng_type * 13194cf49a43SJulian Elischer ng_findtype(const char *typename) 13204cf49a43SJulian Elischer { 13214cf49a43SJulian Elischer struct ng_type *type; 13224cf49a43SJulian Elischer 1323c4282b74SGleb Smirnoff TYPELIST_RLOCK(); 1324069154d5SJulian Elischer LIST_FOREACH(type, &ng_typelist, types) { 13254cf49a43SJulian Elischer if (strcmp(type->name, typename) == 0) 13264cf49a43SJulian Elischer break; 13274cf49a43SJulian Elischer } 1328c4282b74SGleb Smirnoff TYPELIST_RUNLOCK(); 13294cf49a43SJulian Elischer return (type); 13304cf49a43SJulian Elischer } 13314cf49a43SJulian Elischer 13324cf49a43SJulian Elischer /************************************************************************ 13334cf49a43SJulian Elischer Composite routines 13344cf49a43SJulian Elischer ************************************************************************/ 13354cf49a43SJulian Elischer /* 13366b795970SJulian Elischer * Connect two nodes using the specified hooks, using queued functions. 13374cf49a43SJulian Elischer */ 1338e088dd4cSAlexander Motin static int 1339e088dd4cSAlexander Motin ng_con_part3(node_p node, item_p item, hook_p hook) 13404cf49a43SJulian Elischer { 1341e088dd4cSAlexander Motin int error = 0; 13424cf49a43SJulian Elischer 13436b795970SJulian Elischer /* 13446b795970SJulian Elischer * When we run, we know that the node 'node' is locked for us. 13456b795970SJulian Elischer * Our caller has a reference on the hook. 13466b795970SJulian Elischer * Our caller has a reference on the node. 13476b795970SJulian Elischer * (In this case our caller is ng_apply_item() ). 13486b795970SJulian Elischer * The peer hook has a reference on the hook. 13491acb27c6SJulian Elischer * We are all set up except for the final call to the node, and 13501acb27c6SJulian Elischer * the clearing of the INVALID flag. 13516b795970SJulian Elischer */ 13526b795970SJulian Elischer if (NG_HOOK_NODE(hook) == &ng_deadnode) { 13536b795970SJulian Elischer /* 13546b795970SJulian Elischer * The node must have been freed again since we last visited 13556b795970SJulian Elischer * here. ng_destry_hook() has this effect but nothing else does. 13566b795970SJulian Elischer * We should just release our references and 13576b795970SJulian Elischer * free anything we can think of. 13586b795970SJulian Elischer * Since we know it's been destroyed, and it's our caller 13596b795970SJulian Elischer * that holds the references, just return. 13606b795970SJulian Elischer */ 1361e088dd4cSAlexander Motin ERROUT(ENOENT); 13626b795970SJulian Elischer } 13636b795970SJulian Elischer if (hook->hk_node->nd_type->connect) { 1364e088dd4cSAlexander Motin if ((error = (*hook->hk_node->nd_type->connect) (hook))) { 13656b795970SJulian Elischer ng_destroy_hook(hook); /* also zaps peer */ 13661acb27c6SJulian Elischer printf("failed in ng_con_part3()\n"); 1367e088dd4cSAlexander Motin ERROUT(error); 13684cf49a43SJulian Elischer } 13696b795970SJulian Elischer } 13706b795970SJulian Elischer /* 13716b795970SJulian Elischer * XXX this is wrong for SMP. Possibly we need 13726b795970SJulian Elischer * to separate out 'create' and 'invalid' flags. 13736b795970SJulian Elischer * should only set flags on hooks we have locked under our node. 13746b795970SJulian Elischer */ 13756b795970SJulian Elischer hook->hk_flags &= ~HK_INVALID; 1376e088dd4cSAlexander Motin done: 1377e088dd4cSAlexander Motin NG_FREE_ITEM(item); 1378e088dd4cSAlexander Motin return (error); 13796b795970SJulian Elischer } 13806b795970SJulian Elischer 1381e088dd4cSAlexander Motin static int 1382e088dd4cSAlexander Motin ng_con_part2(node_p node, item_p item, hook_p hook) 13836b795970SJulian Elischer { 1384ac5dd141SGleb Smirnoff hook_p peer; 1385e088dd4cSAlexander Motin int error = 0; 13866b795970SJulian Elischer 13876b795970SJulian Elischer /* 13886b795970SJulian Elischer * When we run, we know that the node 'node' is locked for us. 13896b795970SJulian Elischer * Our caller has a reference on the hook. 13906b795970SJulian Elischer * Our caller has a reference on the node. 13916b795970SJulian Elischer * (In this case our caller is ng_apply_item() ). 13926b795970SJulian Elischer * The peer hook has a reference on the hook. 13936b795970SJulian Elischer * our node pointer points to the 'dead' node. 13946b795970SJulian Elischer * First check the hook name is unique. 13951acb27c6SJulian Elischer * Should not happen because we checked before queueing this. 13966b795970SJulian Elischer */ 13976b795970SJulian Elischer if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) { 13986b795970SJulian Elischer TRAP_ERROR(); 13996b795970SJulian Elischer ng_destroy_hook(hook); /* should destroy peer too */ 14001acb27c6SJulian Elischer printf("failed in ng_con_part2()\n"); 1401e088dd4cSAlexander Motin ERROUT(EEXIST); 14026b795970SJulian Elischer } 14036b795970SJulian Elischer /* 14046b795970SJulian Elischer * Check if the node type code has something to say about it 14056b795970SJulian Elischer * If it fails, the unref of the hook will also unref the attached node, 14066b795970SJulian Elischer * however since that node is 'ng_deadnode' this will do nothing. 14076b795970SJulian Elischer * The peer hook will also be destroyed. 14086b795970SJulian Elischer */ 14096b795970SJulian Elischer if (node->nd_type->newhook != NULL) { 1410e088dd4cSAlexander Motin if ((error = (*node->nd_type->newhook)(node, hook, 1411e088dd4cSAlexander Motin hook->hk_name))) { 14126b795970SJulian Elischer ng_destroy_hook(hook); /* should destroy peer too */ 14131acb27c6SJulian Elischer printf("failed in ng_con_part2()\n"); 1414e088dd4cSAlexander Motin ERROUT(error); 14156b795970SJulian Elischer } 14166b795970SJulian Elischer } 14176b795970SJulian Elischer 14186b795970SJulian Elischer /* 14196b795970SJulian Elischer * The 'type' agrees so far, so go ahead and link it in. 14206b795970SJulian Elischer * We'll ask again later when we actually connect the hooks. 14216b795970SJulian Elischer */ 14226b795970SJulian Elischer hook->hk_node = node; /* just overwrite ng_deadnode */ 14236b795970SJulian Elischer NG_NODE_REF(node); /* each hook counts as a reference */ 14246b795970SJulian Elischer LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); 14256b795970SJulian Elischer node->nd_numhooks++; 14266b795970SJulian Elischer NG_HOOK_REF(hook); /* one for the node */ 14276b795970SJulian Elischer 14286b795970SJulian Elischer /* 142964efc707SRobert Watson * We now have a symmetrical situation, where both hooks have been 14305951069aSJulian Elischer * linked to their nodes, the newhook methods have been called 14316b795970SJulian Elischer * And the references are all correct. The hooks are still marked 14326b795970SJulian Elischer * as invalid, as we have not called the 'connect' methods 14336b795970SJulian Elischer * yet. 143464efc707SRobert Watson * We can call the local one immediately as we have the 14356b795970SJulian Elischer * node locked, but we need to queue the remote one. 14366b795970SJulian Elischer */ 14376b795970SJulian Elischer if (hook->hk_node->nd_type->connect) { 1438e088dd4cSAlexander Motin if ((error = (*hook->hk_node->nd_type->connect) (hook))) { 14396b795970SJulian Elischer ng_destroy_hook(hook); /* also zaps peer */ 14401acb27c6SJulian Elischer printf("failed in ng_con_part2(A)\n"); 1441e088dd4cSAlexander Motin ERROUT(error); 14426b795970SJulian Elischer } 14436b795970SJulian Elischer } 1444ac5dd141SGleb Smirnoff 1445ac5dd141SGleb Smirnoff /* 1446ac5dd141SGleb Smirnoff * Acquire topo mutex to avoid race with ng_destroy_hook(). 1447ac5dd141SGleb Smirnoff */ 1448*d2fd0788SAlexander V. Chernikov TOPOLOGY_RLOCK(); 1449ac5dd141SGleb Smirnoff peer = hook->hk_peer; 1450ac5dd141SGleb Smirnoff if (peer == &ng_deadhook) { 1451*d2fd0788SAlexander V. Chernikov TOPOLOGY_RUNLOCK(); 1452ac5dd141SGleb Smirnoff printf("failed in ng_con_part2(B)\n"); 1453ac5dd141SGleb Smirnoff ng_destroy_hook(hook); 1454e088dd4cSAlexander Motin ERROUT(ENOENT); 1455ac5dd141SGleb Smirnoff } 1456*d2fd0788SAlexander V. Chernikov TOPOLOGY_RUNLOCK(); 1457ac5dd141SGleb Smirnoff 1458b332b91fSGleb Smirnoff if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3, 1459b332b91fSGleb Smirnoff NULL, 0, NG_REUSE_ITEM))) { 1460ac5dd141SGleb Smirnoff printf("failed in ng_con_part2(C)\n"); 14611acb27c6SJulian Elischer ng_destroy_hook(hook); /* also zaps peer */ 1462e088dd4cSAlexander Motin return (error); /* item was consumed. */ 14631acb27c6SJulian Elischer } 14646b795970SJulian Elischer hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */ 1465e088dd4cSAlexander Motin return (0); /* item was consumed. */ 1466e088dd4cSAlexander Motin done: 1467e088dd4cSAlexander Motin NG_FREE_ITEM(item); 1468e088dd4cSAlexander Motin return (error); 14694cf49a43SJulian Elischer } 14704cf49a43SJulian Elischer 14714cf49a43SJulian Elischer /* 14726b795970SJulian Elischer * Connect this node with another node. We assume that this node is 14736b795970SJulian Elischer * currently locked, as we are only called from an NGM_CONNECT message. 14744cf49a43SJulian Elischer */ 14756b795970SJulian Elischer static int 1476e088dd4cSAlexander Motin ng_con_nodes(item_p item, node_p node, const char *name, 1477e088dd4cSAlexander Motin node_p node2, const char *name2) 14784cf49a43SJulian Elischer { 14794cf49a43SJulian Elischer int error; 14804cf49a43SJulian Elischer hook_p hook; 14814cf49a43SJulian Elischer hook_p hook2; 14824cf49a43SJulian Elischer 14831acb27c6SJulian Elischer if (ng_findhook(node2, name2) != NULL) { 14841acb27c6SJulian Elischer return(EEXIST); 14851acb27c6SJulian Elischer } 14863e4084c8SJulian Elischer if ((error = ng_add_hook(node, name, &hook))) /* gives us a ref */ 14874cf49a43SJulian Elischer return (error); 14886b795970SJulian Elischer /* Allocate the other hook and link it up */ 14896b795970SJulian Elischer NG_ALLOC_HOOK(hook2); 149019724144SGleb Smirnoff if (hook2 == NULL) { 14916b795970SJulian Elischer TRAP_ERROR(); 14926b795970SJulian Elischer ng_destroy_hook(hook); /* XXX check ref counts so far */ 14936b795970SJulian Elischer NG_HOOK_UNREF(hook); /* including our ref */ 14946b795970SJulian Elischer return (ENOMEM); 14956b795970SJulian Elischer } 14966b795970SJulian Elischer hook2->hk_refs = 1; /* start with a reference for us. */ 14976b795970SJulian Elischer hook2->hk_flags = HK_INVALID; 14986b795970SJulian Elischer hook2->hk_peer = hook; /* Link the two together */ 14996b795970SJulian Elischer hook->hk_peer = hook2; 15006b795970SJulian Elischer NG_HOOK_REF(hook); /* Add a ref for the peer to each*/ 15016b795970SJulian Elischer NG_HOOK_REF(hook2); 15026b795970SJulian Elischer hook2->hk_node = &ng_deadnode; 150387e2c66aSHartmut Brandt strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ); 15046b795970SJulian Elischer 15056b795970SJulian Elischer /* 15066b795970SJulian Elischer * Queue the function above. 15076b795970SJulian Elischer * Procesing continues in that function in the lock context of 15086b795970SJulian Elischer * the other node. 15096b795970SJulian Elischer */ 1510b332b91fSGleb Smirnoff if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0, 1511b332b91fSGleb Smirnoff NG_NOFLAGS))) { 15123fb87c24SAlexander Motin printf("failed in ng_con_nodes(): %d\n", error); 15133fb87c24SAlexander Motin ng_destroy_hook(hook); /* also zaps peer */ 15143fb87c24SAlexander Motin } 15156b795970SJulian Elischer 15166b795970SJulian Elischer NG_HOOK_UNREF(hook); /* Let each hook go if it wants to */ 15176b795970SJulian Elischer NG_HOOK_UNREF(hook2); 15183fb87c24SAlexander Motin return (error); 15194cf49a43SJulian Elischer } 15206b795970SJulian Elischer 15216b795970SJulian Elischer /* 15226b795970SJulian Elischer * Make a peer and connect. 15236b795970SJulian Elischer * We assume that the local node is locked. 15246b795970SJulian Elischer * The new node probably doesn't need a lock until 15256b795970SJulian Elischer * it has a hook, because it cannot really have any work until then, 15266b795970SJulian Elischer * but we should think about it a bit more. 15276b795970SJulian Elischer * 15286b795970SJulian Elischer * The problem may come if the other node also fires up 15296b795970SJulian Elischer * some hardware or a timer or some other source of activation, 15306b795970SJulian Elischer * also it may already get a command msg via it's ID. 15316b795970SJulian Elischer * 15326b795970SJulian Elischer * We could use the same method as ng_con_nodes() but we'd have 15336b795970SJulian Elischer * to add ability to remove the node when failing. (Not hard, just 15346b795970SJulian Elischer * make arg1 point to the node to remove). 15356b795970SJulian Elischer * Unless of course we just ignore failure to connect and leave 15366b795970SJulian Elischer * an unconnected node? 15376b795970SJulian Elischer */ 15386b795970SJulian Elischer static int 15396b795970SJulian Elischer ng_mkpeer(node_p node, const char *name, const char *name2, char *type) 15406b795970SJulian Elischer { 15416b795970SJulian Elischer node_p node2; 15424c9b5910SGleb Smirnoff hook_p hook1, hook2; 15436b795970SJulian Elischer int error; 15446b795970SJulian Elischer 15456b795970SJulian Elischer if ((error = ng_make_node(type, &node2))) { 15466b795970SJulian Elischer return (error); 15476b795970SJulian Elischer } 15486b795970SJulian Elischer 15496b795970SJulian Elischer if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */ 15501acb27c6SJulian Elischer ng_rmnode(node2, NULL, NULL, 0); 15516b795970SJulian Elischer return (error); 15526b795970SJulian Elischer } 15536b795970SJulian Elischer 15546b795970SJulian Elischer if ((error = ng_add_hook(node2, name2, &hook2))) { 15551acb27c6SJulian Elischer ng_rmnode(node2, NULL, NULL, 0); 15566b795970SJulian Elischer ng_destroy_hook(hook1); 15576b795970SJulian Elischer NG_HOOK_UNREF(hook1); 15586b795970SJulian Elischer return (error); 15596b795970SJulian Elischer } 15606b795970SJulian Elischer 15616b795970SJulian Elischer /* 15626b795970SJulian Elischer * Actually link the two hooks together. 15636b795970SJulian Elischer */ 15646b795970SJulian Elischer hook1->hk_peer = hook2; 15656b795970SJulian Elischer hook2->hk_peer = hook1; 15666b795970SJulian Elischer 15676b795970SJulian Elischer /* Each hook is referenced by the other */ 15686b795970SJulian Elischer NG_HOOK_REF(hook1); 15696b795970SJulian Elischer NG_HOOK_REF(hook2); 15706b795970SJulian Elischer 15716b795970SJulian Elischer /* Give each node the opportunity to veto the pending connection */ 15726b795970SJulian Elischer if (hook1->hk_node->nd_type->connect) { 15736b795970SJulian Elischer error = (*hook1->hk_node->nd_type->connect) (hook1); 15746b795970SJulian Elischer } 15756b795970SJulian Elischer 15766b795970SJulian Elischer if ((error == 0) && hook2->hk_node->nd_type->connect) { 15776b795970SJulian Elischer error = (*hook2->hk_node->nd_type->connect) (hook2); 15786b795970SJulian Elischer 15796b795970SJulian Elischer } 15803e4084c8SJulian Elischer 15813e4084c8SJulian Elischer /* 15823e4084c8SJulian Elischer * drop the references we were holding on the two hooks. 15833e4084c8SJulian Elischer */ 15846b795970SJulian Elischer if (error) { 15856b795970SJulian Elischer ng_destroy_hook(hook2); /* also zaps hook1 */ 15861acb27c6SJulian Elischer ng_rmnode(node2, NULL, NULL, 0); 15876b795970SJulian Elischer } else { 15886b795970SJulian Elischer /* As a last act, allow the hooks to be used */ 15896b795970SJulian Elischer hook1->hk_flags &= ~HK_INVALID; 15906b795970SJulian Elischer hook2->hk_flags &= ~HK_INVALID; 15916b795970SJulian Elischer } 15926b795970SJulian Elischer NG_HOOK_UNREF(hook1); 15933e4084c8SJulian Elischer NG_HOOK_UNREF(hook2); 15943e4084c8SJulian Elischer return (error); 15954cf49a43SJulian Elischer } 15966b795970SJulian Elischer 1597069154d5SJulian Elischer /************************************************************************ 1598069154d5SJulian Elischer Utility routines to send self messages 1599069154d5SJulian Elischer ************************************************************************/ 1600069154d5SJulian Elischer 16011acb27c6SJulian Elischer /* Shut this node down as soon as everyone is clear of it */ 160264efc707SRobert Watson /* Should add arg "immediately" to jump the queue */ 1603069154d5SJulian Elischer int 160415cea89fSAlexander Motin ng_rmnode_self(node_p node) 1605069154d5SJulian Elischer { 16061acb27c6SJulian Elischer int error; 16074cf49a43SJulian Elischer 16081acb27c6SJulian Elischer if (node == &ng_deadnode) 16091acb27c6SJulian Elischer return (0); 1610be4252b3SJulian Elischer node->nd_flags |= NGF_INVALID; 1611be4252b3SJulian Elischer if (node->nd_flags & NGF_CLOSING) 16121acb27c6SJulian Elischer return (0); 1613069154d5SJulian Elischer 161415cea89fSAlexander Motin error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0); 16151acb27c6SJulian Elischer return (error); 1616069154d5SJulian Elischer } 1617069154d5SJulian Elischer 16181acb27c6SJulian Elischer static void 16196b795970SJulian Elischer ng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2) 16206b795970SJulian Elischer { 16216b795970SJulian Elischer ng_destroy_hook(hook); 16221acb27c6SJulian Elischer return ; 16236b795970SJulian Elischer } 16246b795970SJulian Elischer 1625954c4772SJulian Elischer int 1626954c4772SJulian Elischer ng_rmhook_self(hook_p hook) 1627954c4772SJulian Elischer { 16286b795970SJulian Elischer int error; 1629954c4772SJulian Elischer node_p node = NG_HOOK_NODE(hook); 1630954c4772SJulian Elischer 16316b795970SJulian Elischer if (node == &ng_deadnode) 16326b795970SJulian Elischer return (0); 16336b795970SJulian Elischer 16346b795970SJulian Elischer error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0); 16356b795970SJulian Elischer return (error); 1636954c4772SJulian Elischer } 1637954c4772SJulian Elischer 1638069154d5SJulian Elischer /*********************************************************************** 16394cf49a43SJulian Elischer * Parse and verify a string of the form: <NODE:><PATH> 16404cf49a43SJulian Elischer * 16414cf49a43SJulian Elischer * Such a string can refer to a specific node or a specific hook 16424cf49a43SJulian Elischer * on a specific node, depending on how you look at it. In the 16434cf49a43SJulian Elischer * latter case, the PATH component must not end in a dot. 16444cf49a43SJulian Elischer * 16454cf49a43SJulian Elischer * Both <NODE:> and <PATH> are optional. The <PATH> is a string 16464cf49a43SJulian Elischer * of hook names separated by dots. This breaks out the original 16474cf49a43SJulian Elischer * string, setting *nodep to "NODE" (or NULL if none) and *pathp 16484cf49a43SJulian Elischer * to "PATH" (or NULL if degenerate). Also, *hookp will point to 16494cf49a43SJulian Elischer * the final hook component of <PATH>, if any, otherwise NULL. 16504cf49a43SJulian Elischer * 16514cf49a43SJulian Elischer * This returns -1 if the path is malformed. The char ** are optional. 1652069154d5SJulian Elischer ***********************************************************************/ 16534cf49a43SJulian Elischer int 16544cf49a43SJulian Elischer ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 16554cf49a43SJulian Elischer { 16564cf49a43SJulian Elischer char *node, *path, *hook; 16574cf49a43SJulian Elischer int k; 16584cf49a43SJulian Elischer 16594cf49a43SJulian Elischer /* 16604cf49a43SJulian Elischer * Extract absolute NODE, if any 16614cf49a43SJulian Elischer */ 16624cf49a43SJulian Elischer for (path = addr; *path && *path != ':'; path++); 16634cf49a43SJulian Elischer if (*path) { 16644cf49a43SJulian Elischer node = addr; /* Here's the NODE */ 16654cf49a43SJulian Elischer *path++ = '\0'; /* Here's the PATH */ 16664cf49a43SJulian Elischer 16674cf49a43SJulian Elischer /* Node name must not be empty */ 16684cf49a43SJulian Elischer if (!*node) 16694cf49a43SJulian Elischer return -1; 16704cf49a43SJulian Elischer 16714cf49a43SJulian Elischer /* A name of "." is OK; otherwise '.' not allowed */ 16724cf49a43SJulian Elischer if (strcmp(node, ".") != 0) { 16734cf49a43SJulian Elischer for (k = 0; node[k]; k++) 16744cf49a43SJulian Elischer if (node[k] == '.') 16754cf49a43SJulian Elischer return -1; 16764cf49a43SJulian Elischer } 16774cf49a43SJulian Elischer } else { 16784cf49a43SJulian Elischer node = NULL; /* No absolute NODE */ 16794cf49a43SJulian Elischer path = addr; /* Here's the PATH */ 16804cf49a43SJulian Elischer } 16814cf49a43SJulian Elischer 16824cf49a43SJulian Elischer /* Snoop for illegal characters in PATH */ 16834cf49a43SJulian Elischer for (k = 0; path[k]; k++) 16844cf49a43SJulian Elischer if (path[k] == ':') 16854cf49a43SJulian Elischer return -1; 16864cf49a43SJulian Elischer 16874cf49a43SJulian Elischer /* Check for no repeated dots in PATH */ 16884cf49a43SJulian Elischer for (k = 0; path[k]; k++) 16894cf49a43SJulian Elischer if (path[k] == '.' && path[k + 1] == '.') 16904cf49a43SJulian Elischer return -1; 16914cf49a43SJulian Elischer 16924cf49a43SJulian Elischer /* Remove extra (degenerate) dots from beginning or end of PATH */ 16934cf49a43SJulian Elischer if (path[0] == '.') 16944cf49a43SJulian Elischer path++; 16954cf49a43SJulian Elischer if (*path && path[strlen(path) - 1] == '.') 16964cf49a43SJulian Elischer path[strlen(path) - 1] = 0; 16974cf49a43SJulian Elischer 16984cf49a43SJulian Elischer /* If PATH has a dot, then we're not talking about a hook */ 16994cf49a43SJulian Elischer if (*path) { 17004cf49a43SJulian Elischer for (hook = path, k = 0; path[k]; k++) 17014cf49a43SJulian Elischer if (path[k] == '.') { 17024cf49a43SJulian Elischer hook = NULL; 17034cf49a43SJulian Elischer break; 17044cf49a43SJulian Elischer } 17054cf49a43SJulian Elischer } else 17064cf49a43SJulian Elischer path = hook = NULL; 17074cf49a43SJulian Elischer 17084cf49a43SJulian Elischer /* Done */ 17094cf49a43SJulian Elischer if (nodep) 17104cf49a43SJulian Elischer *nodep = node; 17114cf49a43SJulian Elischer if (pathp) 17124cf49a43SJulian Elischer *pathp = path; 17134cf49a43SJulian Elischer if (hookp) 17144cf49a43SJulian Elischer *hookp = hook; 17154cf49a43SJulian Elischer return (0); 17164cf49a43SJulian Elischer } 17174cf49a43SJulian Elischer 17184cf49a43SJulian Elischer /* 17194cf49a43SJulian Elischer * Given a path, which may be absolute or relative, and a starting node, 1720069154d5SJulian Elischer * return the destination node. 17214cf49a43SJulian Elischer */ 17224cf49a43SJulian Elischer int 17234bd1b557SGleb Smirnoff ng_path2noderef(node_p here, const char *address, node_p *destp, 17244bd1b557SGleb Smirnoff hook_p *lasthook) 17254cf49a43SJulian Elischer { 172687e2c66aSHartmut Brandt char fullpath[NG_PATHSIZ]; 1727a7da736aSGleb Smirnoff char *nodename, *path; 1728069154d5SJulian Elischer node_p node, oldnode; 17294cf49a43SJulian Elischer 17304cf49a43SJulian Elischer /* Initialize */ 173130400f03SJulian Elischer if (destp == NULL) { 17326b795970SJulian Elischer TRAP_ERROR(); 17334cf49a43SJulian Elischer return EINVAL; 173430400f03SJulian Elischer } 17354cf49a43SJulian Elischer *destp = NULL; 17364cf49a43SJulian Elischer 17374cf49a43SJulian Elischer /* Make a writable copy of address for ng_path_parse() */ 17384cf49a43SJulian Elischer strncpy(fullpath, address, sizeof(fullpath) - 1); 17394cf49a43SJulian Elischer fullpath[sizeof(fullpath) - 1] = '\0'; 17404cf49a43SJulian Elischer 17414cf49a43SJulian Elischer /* Parse out node and sequence of hooks */ 17424cf49a43SJulian Elischer if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 17436b795970SJulian Elischer TRAP_ERROR(); 17444cf49a43SJulian Elischer return EINVAL; 17454cf49a43SJulian Elischer } 17464cf49a43SJulian Elischer 1747069154d5SJulian Elischer /* 1748069154d5SJulian Elischer * For an absolute address, jump to the starting node. 1749069154d5SJulian Elischer * Note that this holds a reference on the node for us. 1750069154d5SJulian Elischer * Don't forget to drop the reference if we don't need it. 1751069154d5SJulian Elischer */ 17524cf49a43SJulian Elischer if (nodename) { 1753069154d5SJulian Elischer node = ng_name2noderef(here, nodename); 17544cf49a43SJulian Elischer if (node == NULL) { 17556b795970SJulian Elischer TRAP_ERROR(); 17564cf49a43SJulian Elischer return (ENOENT); 17574cf49a43SJulian Elischer } 1758069154d5SJulian Elischer } else { 1759069154d5SJulian Elischer if (here == NULL) { 17606b795970SJulian Elischer TRAP_ERROR(); 1761069154d5SJulian Elischer return (EINVAL); 1762069154d5SJulian Elischer } 17634cf49a43SJulian Elischer node = here; 176430400f03SJulian Elischer NG_NODE_REF(node); 1765069154d5SJulian Elischer } 17664cf49a43SJulian Elischer 1767a7da736aSGleb Smirnoff if (path == NULL) { 1768a7da736aSGleb Smirnoff if (lasthook != NULL) 1769a7da736aSGleb Smirnoff *lasthook = NULL; 1770a7da736aSGleb Smirnoff *destp = node; 1771a7da736aSGleb Smirnoff return (0); 1772a7da736aSGleb Smirnoff } 1773a7da736aSGleb Smirnoff 1774069154d5SJulian Elischer /* 1775069154d5SJulian Elischer * Now follow the sequence of hooks 1776a7da736aSGleb Smirnoff * 1777a7da736aSGleb Smirnoff * XXXGL: The path may demolish as we go the sequence, but if 1778a7da736aSGleb Smirnoff * we hold the topology mutex at critical places, then, I hope, 1779a7da736aSGleb Smirnoff * we would always have valid pointers in hand, although the 1780a7da736aSGleb Smirnoff * path behind us may no longer exist. 1781069154d5SJulian Elischer */ 1782a7da736aSGleb Smirnoff for (;;) { 1783a7da736aSGleb Smirnoff hook_p hook; 17844cf49a43SJulian Elischer char *segment; 17854cf49a43SJulian Elischer 17864cf49a43SJulian Elischer /* 17874cf49a43SJulian Elischer * Break out the next path segment. Replace the dot we just 1788a7da736aSGleb Smirnoff * found with a NUL; "path" points to the next segment (or the 17894cf49a43SJulian Elischer * NUL at the end). 17904cf49a43SJulian Elischer */ 1791a7da736aSGleb Smirnoff for (segment = path; *path != '\0'; path++) { 1792a7da736aSGleb Smirnoff if (*path == '.') { 1793a7da736aSGleb Smirnoff *path++ = '\0'; 17944cf49a43SJulian Elischer break; 17954cf49a43SJulian Elischer } 17964cf49a43SJulian Elischer } 17974cf49a43SJulian Elischer 17984cf49a43SJulian Elischer /* We have a segment, so look for a hook by that name */ 1799899e9c4eSArchie Cobbs hook = ng_findhook(node, segment); 18004cf49a43SJulian Elischer 1801*d2fd0788SAlexander V. Chernikov TOPOLOGY_WLOCK(); 18024cf49a43SJulian Elischer /* Can't get there from here... */ 18034bd1b557SGleb Smirnoff if (hook == NULL || NG_HOOK_PEER(hook) == NULL || 18044bd1b557SGleb Smirnoff NG_HOOK_NOT_VALID(hook) || 18054bd1b557SGleb Smirnoff NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) { 18066b795970SJulian Elischer TRAP_ERROR(); 180730400f03SJulian Elischer NG_NODE_UNREF(node); 1808*d2fd0788SAlexander V. Chernikov TOPOLOGY_WUNLOCK(); 18094cf49a43SJulian Elischer return (ENOENT); 18104cf49a43SJulian Elischer } 18114cf49a43SJulian Elischer 1812069154d5SJulian Elischer /* 1813069154d5SJulian Elischer * Hop on over to the next node 1814069154d5SJulian Elischer * XXX 1815069154d5SJulian Elischer * Big race conditions here as hooks and nodes go away 1816069154d5SJulian Elischer * *** Idea.. store an ng_ID_t in each hook and use that 1817069154d5SJulian Elischer * instead of the direct hook in this crawl? 1818069154d5SJulian Elischer */ 1819069154d5SJulian Elischer oldnode = node; 182030400f03SJulian Elischer if ((node = NG_PEER_NODE(hook))) 182130400f03SJulian Elischer NG_NODE_REF(node); /* XXX RACE */ 182230400f03SJulian Elischer NG_NODE_UNREF(oldnode); /* XXX another race */ 182330400f03SJulian Elischer if (NG_NODE_NOT_VALID(node)) { 182430400f03SJulian Elischer NG_NODE_UNREF(node); /* XXX more races */ 1825*d2fd0788SAlexander V. Chernikov TOPOLOGY_WUNLOCK(); 18266b795970SJulian Elischer TRAP_ERROR(); 18274cf49a43SJulian Elischer return (ENXIO); 18284cf49a43SJulian Elischer } 18294cf49a43SJulian Elischer 1830a7da736aSGleb Smirnoff if (*path == '\0') { 1831a7da736aSGleb Smirnoff if (lasthook != NULL) { 1832a7da736aSGleb Smirnoff if (hook != NULL) { 1833a7da736aSGleb Smirnoff *lasthook = NG_HOOK_PEER(hook); 1834a7da736aSGleb Smirnoff NG_HOOK_REF(*lasthook); 1835a7da736aSGleb Smirnoff } else 1836a7da736aSGleb Smirnoff *lasthook = NULL; 1837a7da736aSGleb Smirnoff } 1838*d2fd0788SAlexander V. Chernikov TOPOLOGY_WUNLOCK(); 18394cf49a43SJulian Elischer *destp = node; 1840069154d5SJulian Elischer return (0); 1841069154d5SJulian Elischer } 1842*d2fd0788SAlexander V. Chernikov TOPOLOGY_WUNLOCK(); 1843a7da736aSGleb Smirnoff } 1844a7da736aSGleb Smirnoff } 1845069154d5SJulian Elischer 1846069154d5SJulian Elischer /***************************************************************\ 1847069154d5SJulian Elischer * Input queue handling. 1848069154d5SJulian Elischer * All activities are submitted to the node via the input queue 1849069154d5SJulian Elischer * which implements a multiple-reader/single-writer gate. 185064efc707SRobert Watson * Items which cannot be handled immediately are queued. 1851069154d5SJulian Elischer * 1852069154d5SJulian Elischer * read-write queue locking inline functions * 1853069154d5SJulian Elischer \***************************************************************/ 1854069154d5SJulian Elischer 18559852972bSAlexander Motin static __inline void ng_queue_rw(node_p node, item_p item, int rw); 18569852972bSAlexander Motin static __inline item_p ng_dequeue(node_p node, int *rw); 18579852972bSAlexander Motin static __inline item_p ng_acquire_read(node_p node, item_p item); 18589852972bSAlexander Motin static __inline item_p ng_acquire_write(node_p node, item_p item); 18599852972bSAlexander Motin static __inline void ng_leave_read(node_p node); 18609852972bSAlexander Motin static __inline void ng_leave_write(node_p node); 1861069154d5SJulian Elischer 1862069154d5SJulian Elischer /* 1863069154d5SJulian Elischer * Definition of the bits fields in the ng_queue flag word. 1864069154d5SJulian Elischer * Defined here rather than in netgraph.h because no-one should fiddle 1865069154d5SJulian Elischer * with them. 1866069154d5SJulian Elischer * 1867b57a7965SJulian Elischer * The ordering here may be important! don't shuffle these. 1868069154d5SJulian Elischer */ 1869069154d5SJulian Elischer /*- 1870069154d5SJulian Elischer Safety Barrier--------+ (adjustable to suit taste) (not used yet) 1871069154d5SJulian Elischer | 1872069154d5SJulian Elischer V 1873069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+ 18744be59335SGleb Smirnoff | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 18754be59335SGleb Smirnoff | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A| 18764be59335SGleb Smirnoff | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W| 1877069154d5SJulian Elischer +-------+-------+-------+-------+-------+-------+-------+-------+ 18784be59335SGleb Smirnoff \___________________________ ____________________________/ | | 18794be59335SGleb Smirnoff V | | 18804be59335SGleb Smirnoff [active reader count] | | 1881069154d5SJulian Elischer | | 18824be59335SGleb Smirnoff Operation Pending -------------------------------+ | 1883069154d5SJulian Elischer | 18844be59335SGleb Smirnoff Active Writer ---------------------------------------+ 1885b57a7965SJulian Elischer 18868f9ac44aSAlexander Motin Node queue has such semantics: 18878f9ac44aSAlexander Motin - All flags modifications are atomic. 18888f9ac44aSAlexander Motin - Reader count can be incremented only if there is no writer or pending flags. 18898f9ac44aSAlexander Motin As soon as this can't be done with single operation, it is implemented with 18908f9ac44aSAlexander Motin spin loop and atomic_cmpset(). 18918f9ac44aSAlexander Motin - Writer flag can be set only if there is no any bits set. 18928f9ac44aSAlexander Motin It is implemented with atomic_cmpset(). 18938f9ac44aSAlexander Motin - Pending flag can be set any time, but to avoid collision on queue processing 18948f9ac44aSAlexander Motin all queue fields are protected by the mutex. 18958f9ac44aSAlexander Motin - Queue processing thread reads queue holding the mutex, but releases it while 18968f9ac44aSAlexander Motin processing. When queue is empty pending flag is removed. 1897069154d5SJulian Elischer */ 18988f9ac44aSAlexander Motin 18994be59335SGleb Smirnoff #define WRITER_ACTIVE 0x00000001 19004be59335SGleb Smirnoff #define OP_PENDING 0x00000002 19014be59335SGleb Smirnoff #define READER_INCREMENT 0x00000004 19024be59335SGleb Smirnoff #define READER_MASK 0xfffffffc /* Not valid if WRITER_ACTIVE is set */ 19034be59335SGleb Smirnoff #define SAFETY_BARRIER 0x00100000 /* 128K items queued should be enough */ 1904b57a7965SJulian Elischer 1905b57a7965SJulian Elischer /* Defines of more elaborate states on the queue */ 19064be59335SGleb Smirnoff /* Mask of bits a new read cares about */ 19074be59335SGleb Smirnoff #define NGQ_RMASK (WRITER_ACTIVE|OP_PENDING) 1908b57a7965SJulian Elischer 19094be59335SGleb Smirnoff /* Mask of bits a new write cares about */ 1910b57a7965SJulian Elischer #define NGQ_WMASK (NGQ_RMASK|READER_MASK) 1911b57a7965SJulian Elischer 19124be59335SGleb Smirnoff /* Test to decide if there is something on the queue. */ 19134be59335SGleb Smirnoff #define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING) 19144be59335SGleb Smirnoff 19154be59335SGleb Smirnoff /* How to decide what the next queued item is. */ 19169852972bSAlexander Motin #define HEAD_IS_READER(QP) NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue)) 19179852972bSAlexander Motin #define HEAD_IS_WRITER(QP) NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */ 19184be59335SGleb Smirnoff 19194be59335SGleb Smirnoff /* Read the status to decide if the next item on the queue can now run. */ 19204be59335SGleb Smirnoff #define QUEUED_READER_CAN_PROCEED(QP) \ 19214be59335SGleb Smirnoff (((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0) 19224be59335SGleb Smirnoff #define QUEUED_WRITER_CAN_PROCEED(QP) \ 19234be59335SGleb Smirnoff (((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0) 1924b57a7965SJulian Elischer 1925b57a7965SJulian Elischer /* Is there a chance of getting ANY work off the queue? */ 19264be59335SGleb Smirnoff #define NEXT_QUEUED_ITEM_CAN_PROCEED(QP) \ 19274be59335SGleb Smirnoff ((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) : \ 1928394cb30aSAlexander Motin QUEUED_WRITER_CAN_PROCEED(QP)) 19294be59335SGleb Smirnoff 1930714fb865SGleb Smirnoff #define NGQRW_R 0 1931714fb865SGleb Smirnoff #define NGQRW_W 1 1932714fb865SGleb Smirnoff 19339852972bSAlexander Motin #define NGQ2_WORKQ 0x00000001 19349852972bSAlexander Motin 1935069154d5SJulian Elischer /* 1936069154d5SJulian Elischer * Taking into account the current state of the queue and node, possibly take 1937069154d5SJulian Elischer * the next entry off the queue and return it. Return NULL if there was 1938069154d5SJulian Elischer * nothing we could return, either because there really was nothing there, or 1939069154d5SJulian Elischer * because the node was in a state where it cannot yet process the next item 1940069154d5SJulian Elischer * on the queue. 1941069154d5SJulian Elischer */ 1942069154d5SJulian Elischer static __inline item_p 19439852972bSAlexander Motin ng_dequeue(node_p node, int *rw) 1944069154d5SJulian Elischer { 1945069154d5SJulian Elischer item_p item; 19469852972bSAlexander Motin struct ng_queue *ngq = &node->nd_input_queue; 1947b57a7965SJulian Elischer 19488f9ac44aSAlexander Motin /* This MUST be called with the mutex held. */ 1949d58e678fSGleb Smirnoff mtx_assert(&ngq->q_mtx, MA_OWNED); 19508f9ac44aSAlexander Motin 19518f9ac44aSAlexander Motin /* If there is nothing queued, then just return. */ 19524be59335SGleb Smirnoff if (!QUEUE_ACTIVE(ngq)) { 19532955ee18SGleb Smirnoff CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; " 19542955ee18SGleb Smirnoff "queue flags 0x%lx", __func__, 19559852972bSAlexander Motin node->nd_ID, node, ngq->q_flags); 19564be59335SGleb Smirnoff return (NULL); 19574be59335SGleb Smirnoff } 1958d58e678fSGleb Smirnoff 19594be59335SGleb Smirnoff /* 19604be59335SGleb Smirnoff * From here, we can assume there is a head item. 19614be59335SGleb Smirnoff * We need to find out what it is and if it can be dequeued, given 19624be59335SGleb Smirnoff * the current state of the node. 19634be59335SGleb Smirnoff */ 19644be59335SGleb Smirnoff if (HEAD_IS_READER(ngq)) { 1965394cb30aSAlexander Motin while (1) { 1966394cb30aSAlexander Motin long t = ngq->q_flags; 1967394cb30aSAlexander Motin if (t & WRITER_ACTIVE) { 19688f9ac44aSAlexander Motin /* There is writer, reader can't proceed. */ 19694bd1b557SGleb Smirnoff CTR4(KTR_NET, "%20s: node [%x] (%p) queued " 19704bd1b557SGleb Smirnoff "reader can't proceed; queue flags 0x%lx", 19714bd1b557SGleb Smirnoff __func__, node->nd_ID, node, t); 19724be59335SGleb Smirnoff return (NULL); 19734be59335SGleb Smirnoff } 19749852972bSAlexander Motin if (atomic_cmpset_acq_int(&ngq->q_flags, t, 1975394cb30aSAlexander Motin t + READER_INCREMENT)) 1976394cb30aSAlexander Motin break; 1977394cb30aSAlexander Motin cpu_spinwait(); 1978394cb30aSAlexander Motin } 19798f9ac44aSAlexander Motin /* We have got reader lock for the node. */ 1980714fb865SGleb Smirnoff *rw = NGQRW_R; 19819852972bSAlexander Motin } else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING, 1982394cb30aSAlexander Motin OP_PENDING + WRITER_ACTIVE)) { 19838f9ac44aSAlexander Motin /* We have got writer lock for the node. */ 1984714fb865SGleb Smirnoff *rw = NGQRW_W; 1985069154d5SJulian Elischer } else { 19868f9ac44aSAlexander Motin /* There is somebody other, writer can't proceed. */ 19874bd1b557SGleb Smirnoff CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer can't " 19884bd1b557SGleb Smirnoff "proceed; queue flags 0x%lx", __func__, node->nd_ID, node, 19894bd1b557SGleb Smirnoff ngq->q_flags); 19904be59335SGleb Smirnoff return (NULL); 19914cf49a43SJulian Elischer } 19924cf49a43SJulian Elischer 19934cf49a43SJulian Elischer /* 1994069154d5SJulian Elischer * Now we dequeue the request (whatever it may be) and correct the 1995069154d5SJulian Elischer * pending flags and the next and last pointers. 19964cf49a43SJulian Elischer */ 19979852972bSAlexander Motin item = STAILQ_FIRST(&ngq->queue); 19989852972bSAlexander Motin STAILQ_REMOVE_HEAD(&ngq->queue, el_next); 19999852972bSAlexander Motin if (STAILQ_EMPTY(&ngq->queue)) 20009852972bSAlexander Motin atomic_clear_int(&ngq->q_flags, OP_PENDING); 20014bd1b557SGleb Smirnoff CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; queue " 20024bd1b557SGleb Smirnoff "flags 0x%lx", __func__, node->nd_ID, node, item, *rw ? "WRITER" : 20034bd1b557SGleb Smirnoff "READER", ngq->q_flags); 2004069154d5SJulian Elischer return (item); 2005069154d5SJulian Elischer } 2006859a4d16SJulian Elischer 2007859a4d16SJulian Elischer /* 20088f9ac44aSAlexander Motin * Queue a packet to be picked up later by someone else. 20098f9ac44aSAlexander Motin * If the queue could be run now, add node to the queue handler's worklist. 2010859a4d16SJulian Elischer */ 2011069154d5SJulian Elischer static __inline void 20129852972bSAlexander Motin ng_queue_rw(node_p node, item_p item, int rw) 2013069154d5SJulian Elischer { 20149852972bSAlexander Motin struct ng_queue *ngq = &node->nd_input_queue; 20154be59335SGleb Smirnoff if (rw == NGQRW_W) 20164be59335SGleb Smirnoff NGI_SET_WRITER(item); 20174be59335SGleb Smirnoff else 20184be59335SGleb Smirnoff NGI_SET_READER(item); 20193fabe28bSRyan Stone item->depth = 1; 2020394cb30aSAlexander Motin 2021394cb30aSAlexander Motin NG_QUEUE_LOCK(ngq); 2022394cb30aSAlexander Motin /* Set OP_PENDING flag and enqueue the item. */ 20239852972bSAlexander Motin atomic_set_int(&ngq->q_flags, OP_PENDING); 20249852972bSAlexander Motin STAILQ_INSERT_TAIL(&ngq->queue, item, el_next); 2025394cb30aSAlexander Motin 20262955ee18SGleb Smirnoff CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__, 20279852972bSAlexander Motin node->nd_ID, node, item, rw ? "WRITER" : "READER" ); 20284be59335SGleb Smirnoff 20294be59335SGleb Smirnoff /* 20304be59335SGleb Smirnoff * We can take the worklist lock with the node locked 20314be59335SGleb Smirnoff * BUT NOT THE REVERSE! 20324be59335SGleb Smirnoff */ 20334be59335SGleb Smirnoff if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 20349852972bSAlexander Motin ng_worklist_add(node); 2035394cb30aSAlexander Motin NG_QUEUE_UNLOCK(ngq); 2036069154d5SJulian Elischer } 2037069154d5SJulian Elischer 20388f9ac44aSAlexander Motin /* Acquire reader lock on node. If node is busy, queue the packet. */ 2039069154d5SJulian Elischer static __inline item_p 20409852972bSAlexander Motin ng_acquire_read(node_p node, item_p item) 2041069154d5SJulian Elischer { 20429852972bSAlexander Motin KASSERT(node != &ng_deadnode, 2043ac5dd141SGleb Smirnoff ("%s: working on deadnode", __func__)); 2044069154d5SJulian Elischer 2045394cb30aSAlexander Motin /* Reader needs node without writer and pending items. */ 20464bd1b557SGleb Smirnoff for (;;) { 20479852972bSAlexander Motin long t = node->nd_input_queue.q_flags; 2048394cb30aSAlexander Motin if (t & NGQ_RMASK) 2049394cb30aSAlexander Motin break; /* Node is not ready for reader. */ 20504bd1b557SGleb Smirnoff if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, t, 20514bd1b557SGleb Smirnoff t + READER_INCREMENT)) { 2052069154d5SJulian Elischer /* Successfully grabbed node */ 2053394cb30aSAlexander Motin CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", 20549852972bSAlexander Motin __func__, node->nd_ID, node, item); 2055069154d5SJulian Elischer return (item); 2056069154d5SJulian Elischer } 2057394cb30aSAlexander Motin cpu_spinwait(); 2058394cb30aSAlexander Motin }; 2059069154d5SJulian Elischer 2060394cb30aSAlexander Motin /* Queue the request for later. */ 20619852972bSAlexander Motin ng_queue_rw(node, item, NGQRW_R); 2062f912c0f7SGleb Smirnoff 2063f912c0f7SGleb Smirnoff return (NULL); 2064069154d5SJulian Elischer } 2065069154d5SJulian Elischer 20668f9ac44aSAlexander Motin /* Acquire writer lock on node. If node is busy, queue the packet. */ 2067069154d5SJulian Elischer static __inline item_p 20689852972bSAlexander Motin ng_acquire_write(node_p node, item_p item) 2069069154d5SJulian Elischer { 20709852972bSAlexander Motin KASSERT(node != &ng_deadnode, 2071ac5dd141SGleb Smirnoff ("%s: working on deadnode", __func__)); 2072ac5dd141SGleb Smirnoff 2073394cb30aSAlexander Motin /* Writer needs completely idle node. */ 20744bd1b557SGleb Smirnoff if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, 0, 20754bd1b557SGleb Smirnoff WRITER_ACTIVE)) { 2076394cb30aSAlexander Motin /* Successfully grabbed node */ 20772955ee18SGleb Smirnoff CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", 20789852972bSAlexander Motin __func__, node->nd_ID, node, item); 2079069154d5SJulian Elischer return (item); 2080069154d5SJulian Elischer } 2081069154d5SJulian Elischer 2082394cb30aSAlexander Motin /* Queue the request for later. */ 20839852972bSAlexander Motin ng_queue_rw(node, item, NGQRW_W); 2084f912c0f7SGleb Smirnoff 2085f912c0f7SGleb Smirnoff return (NULL); 2086069154d5SJulian Elischer } 2087069154d5SJulian Elischer 2088262dfd7fSJulian Elischer #if 0 2089262dfd7fSJulian Elischer static __inline item_p 20909852972bSAlexander Motin ng_upgrade_write(node_p node, item_p item) 2091262dfd7fSJulian Elischer { 20929852972bSAlexander Motin struct ng_queue *ngq = &node->nd_input_queue; 20939852972bSAlexander Motin KASSERT(node != &ng_deadnode, 2094262dfd7fSJulian Elischer ("%s: working on deadnode", __func__)); 2095262dfd7fSJulian Elischer 2096262dfd7fSJulian Elischer NGI_SET_WRITER(item); 2097262dfd7fSJulian Elischer 20989852972bSAlexander Motin NG_QUEUE_LOCK(ngq); 2099262dfd7fSJulian Elischer 2100262dfd7fSJulian Elischer /* 2101262dfd7fSJulian Elischer * There will never be no readers as we are there ourselves. 2102262dfd7fSJulian Elischer * Set the WRITER_ACTIVE flags ASAP to block out fast track readers. 2103262dfd7fSJulian Elischer * The caller we are running from will call ng_leave_read() 2104262dfd7fSJulian Elischer * soon, so we must account for that. We must leave again with the 2105262dfd7fSJulian Elischer * READER lock. If we find other readers, then 2106262dfd7fSJulian Elischer * queue the request for later. However "later" may be rignt now 2107262dfd7fSJulian Elischer * if there are no readers. We don't really care if there are queued 2108262dfd7fSJulian Elischer * items as we will bypass them anyhow. 2109262dfd7fSJulian Elischer */ 21109852972bSAlexander Motin atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT); 21119852972bSAlexander Motin if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) { 21129852972bSAlexander Motin NG_QUEUE_UNLOCK(ngq); 2113262dfd7fSJulian Elischer 2114262dfd7fSJulian Elischer /* It's just us, act on the item. */ 2115262dfd7fSJulian Elischer /* will NOT drop writer lock when done */ 2116262dfd7fSJulian Elischer ng_apply_item(node, item, 0); 2117262dfd7fSJulian Elischer 2118262dfd7fSJulian Elischer /* 2119262dfd7fSJulian Elischer * Having acted on the item, atomically 21204bd1b557SGleb Smirnoff * downgrade back to READER and finish up. 2121262dfd7fSJulian Elischer */ 21224bd1b557SGleb Smirnoff atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE); 2123262dfd7fSJulian Elischer 2124262dfd7fSJulian Elischer /* Our caller will call ng_leave_read() */ 2125262dfd7fSJulian Elischer return; 2126262dfd7fSJulian Elischer } 2127262dfd7fSJulian Elischer /* 2128262dfd7fSJulian Elischer * It's not just us active, so queue us AT THE HEAD. 2129262dfd7fSJulian Elischer * "Why?" I hear you ask. 2130262dfd7fSJulian Elischer * Put us at the head of the queue as we've already been 2131262dfd7fSJulian Elischer * through it once. If there is nothing else waiting, 2132262dfd7fSJulian Elischer * set the correct flags. 2133262dfd7fSJulian Elischer */ 21349852972bSAlexander Motin if (STAILQ_EMPTY(&ngq->queue)) { 2135262dfd7fSJulian Elischer /* We've gone from, 0 to 1 item in the queue */ 21369852972bSAlexander Motin atomic_set_int(&ngq->q_flags, OP_PENDING); 2137262dfd7fSJulian Elischer 2138262dfd7fSJulian Elischer CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__, 21399852972bSAlexander Motin node->nd_ID, node); 2140262dfd7fSJulian Elischer }; 21419852972bSAlexander Motin STAILQ_INSERT_HEAD(&ngq->queue, item, el_next); 21429852972bSAlexander Motin CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER", 21439852972bSAlexander Motin __func__, node->nd_ID, node, item ); 2144262dfd7fSJulian Elischer 2145262dfd7fSJulian Elischer /* Reverse what we did above. That downgrades us back to reader */ 21469852972bSAlexander Motin atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE); 2147394cb30aSAlexander Motin if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 21489852972bSAlexander Motin ng_worklist_add(node); 21499852972bSAlexander Motin NG_QUEUE_UNLOCK(ngq); 2150262dfd7fSJulian Elischer 2151262dfd7fSJulian Elischer return; 2152262dfd7fSJulian Elischer } 2153262dfd7fSJulian Elischer #endif 2154262dfd7fSJulian Elischer 21558f9ac44aSAlexander Motin /* Release reader lock. */ 2156069154d5SJulian Elischer static __inline void 21579852972bSAlexander Motin ng_leave_read(node_p node) 2158069154d5SJulian Elischer { 21599852972bSAlexander Motin atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT); 2160069154d5SJulian Elischer } 2161069154d5SJulian Elischer 21628f9ac44aSAlexander Motin /* Release writer lock. */ 2163069154d5SJulian Elischer static __inline void 21649852972bSAlexander Motin ng_leave_write(node_p node) 2165069154d5SJulian Elischer { 21669852972bSAlexander Motin atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE); 2167069154d5SJulian Elischer } 2168069154d5SJulian Elischer 21698f9ac44aSAlexander Motin /* Purge node queue. Called on node shutdown. */ 2170069154d5SJulian Elischer static void 21719852972bSAlexander Motin ng_flush_input_queue(node_p node) 2172069154d5SJulian Elischer { 21739852972bSAlexander Motin struct ng_queue *ngq = &node->nd_input_queue; 2174069154d5SJulian Elischer item_p item; 2175069154d5SJulian Elischer 21762c8dda8dSWojciech A. Koszek NG_QUEUE_LOCK(ngq); 21779852972bSAlexander Motin while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) { 21789852972bSAlexander Motin STAILQ_REMOVE_HEAD(&ngq->queue, el_next); 21799852972bSAlexander Motin if (STAILQ_EMPTY(&ngq->queue)) 21809852972bSAlexander Motin atomic_clear_int(&ngq->q_flags, OP_PENDING); 21812c8dda8dSWojciech A. Koszek NG_QUEUE_UNLOCK(ngq); 21824be59335SGleb Smirnoff 21834be59335SGleb Smirnoff /* If the item is supplying a callback, call it with an error */ 218410e87318SAlexander Motin if (item->apply != NULL) { 218510e87318SAlexander Motin if (item->depth == 1) 218610e87318SAlexander Motin item->apply->error = ENOENT; 218710e87318SAlexander Motin if (refcount_release(&item->apply->refs)) { 218810e87318SAlexander Motin (*item->apply->apply)(item->apply->context, 218910e87318SAlexander Motin item->apply->error); 219010e87318SAlexander Motin } 2191eb2405ddSGleb Smirnoff } 2192505fad52SJulian Elischer NG_FREE_ITEM(item); 21932c8dda8dSWojciech A. Koszek NG_QUEUE_LOCK(ngq); 2194069154d5SJulian Elischer } 21952c8dda8dSWojciech A. Koszek NG_QUEUE_UNLOCK(ngq); 2196069154d5SJulian Elischer } 2197069154d5SJulian Elischer 2198069154d5SJulian Elischer /*********************************************************************** 2199069154d5SJulian Elischer * Externally visible method for sending or queueing messages or data. 2200069154d5SJulian Elischer ***********************************************************************/ 2201069154d5SJulian Elischer 2202069154d5SJulian Elischer /* 22031acb27c6SJulian Elischer * The module code should have filled out the item correctly by this stage: 2204069154d5SJulian Elischer * Common: 2205069154d5SJulian Elischer * reference to destination node. 2206069154d5SJulian Elischer * Reference to destination rcv hook if relevant. 2207e088dd4cSAlexander Motin * apply pointer must be or NULL or reference valid struct ng_apply_info. 2208069154d5SJulian Elischer * Data: 2209069154d5SJulian Elischer * pointer to mbuf 2210069154d5SJulian Elischer * Control_Message: 2211069154d5SJulian Elischer * pointer to msg. 2212069154d5SJulian Elischer * ID of original sender node. (return address) 22131acb27c6SJulian Elischer * Function: 22141acb27c6SJulian Elischer * Function pointer 22151acb27c6SJulian Elischer * void * argument 22161acb27c6SJulian Elischer * integer argument 2217069154d5SJulian Elischer * 2218069154d5SJulian Elischer * The nodes have several routines and macros to help with this task: 2219069154d5SJulian Elischer */ 2220069154d5SJulian Elischer 2221069154d5SJulian Elischer int 222242282202SGleb Smirnoff ng_snd_item(item_p item, int flags) 2223069154d5SJulian Elischer { 2224e088dd4cSAlexander Motin hook_p hook; 2225e088dd4cSAlexander Motin node_p node; 222642282202SGleb Smirnoff int queue, rw; 2227e088dd4cSAlexander Motin struct ng_queue *ngq; 222832b33288SGleb Smirnoff int error = 0; 2229069154d5SJulian Elischer 2230f50597f5SAlexander Motin /* We are sending item, so it must be present! */ 2231f50597f5SAlexander Motin KASSERT(item != NULL, ("ng_snd_item: item is NULL")); 2232e088dd4cSAlexander Motin 2233e088dd4cSAlexander Motin #ifdef NETGRAPH_DEBUG 2234e088dd4cSAlexander Motin _ngi_check(item, __FILE__, __LINE__); 2235e088dd4cSAlexander Motin #endif 2236e088dd4cSAlexander Motin 2237f50597f5SAlexander Motin /* Item was sent once more, postpone apply() call. */ 2238e088dd4cSAlexander Motin if (item->apply) 2239e088dd4cSAlexander Motin refcount_acquire(&item->apply->refs); 2240e088dd4cSAlexander Motin 2241e088dd4cSAlexander Motin node = NGI_NODE(item); 2242f50597f5SAlexander Motin /* Node is never optional. */ 2243f50597f5SAlexander Motin KASSERT(node != NULL, ("ng_snd_item: node is NULL")); 2244e088dd4cSAlexander Motin 2245e72a98f4SAlexander Motin hook = NGI_HOOK(item); 2246f50597f5SAlexander Motin /* Valid hook and mbuf are mandatory for data. */ 2247f50597f5SAlexander Motin if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) { 2248f50597f5SAlexander Motin KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL")); 224910204449SJulian Elischer if (NGI_M(item) == NULL) 2250e088dd4cSAlexander Motin ERROUT(EINVAL); 2251069154d5SJulian Elischer CHECK_DATA_MBUF(NGI_M(item)); 22526f683eeeSGleb Smirnoff } 22536f683eeeSGleb Smirnoff 2254069154d5SJulian Elischer /* 2255f50597f5SAlexander Motin * If the item or the node specifies single threading, force 2256f50597f5SAlexander Motin * writer semantics. Similarly, the node may say one hook always 2257f50597f5SAlexander Motin * produces writers. These are overrides. 2258069154d5SJulian Elischer */ 2259db3408aeSAlexander Motin if (((item->el_flags & NGQF_RW) == NGQF_WRITER) || 2260f50597f5SAlexander Motin (node->nd_flags & NGF_FORCE_WRITER) || 2261f50597f5SAlexander Motin (hook && (hook->hk_flags & HK_FORCE_WRITER))) { 2262069154d5SJulian Elischer rw = NGQRW_W; 2263f50597f5SAlexander Motin } else { 2264f50597f5SAlexander Motin rw = NGQRW_R; 2265f50597f5SAlexander Motin } 22666f683eeeSGleb Smirnoff 226781a253a4SAlexander Motin /* 2268f089869fSMarko Zec * If sender or receiver requests queued delivery, or call graph 2269f089869fSMarko Zec * loops back from outbound to inbound path, or stack usage 227081a253a4SAlexander Motin * level is dangerous - enqueue message. 227181a253a4SAlexander Motin */ 227281a253a4SAlexander Motin if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) { 227381a253a4SAlexander Motin queue = 1; 2274f089869fSMarko Zec } else if (hook && (hook->hk_flags & HK_TO_INBOUND) && 2275f089869fSMarko Zec curthread->td_ng_outbound) { 2276f089869fSMarko Zec queue = 1; 2277f50597f5SAlexander Motin } else { 2278f50597f5SAlexander Motin queue = 0; 227981a253a4SAlexander Motin #ifdef GET_STACK_USAGE 2280d4529f98SAlexander Motin /* 2281942fe01fSDmitry Morozovsky * Most of netgraph nodes have small stack consumption and 2282f50597f5SAlexander Motin * for them 25% of free stack space is more than enough. 2283d4529f98SAlexander Motin * Nodes/hooks with higher stack usage should be marked as 2284f9773372SDmitry Morozovsky * HI_STACK. For them 50% of stack will be guaranteed then. 2285f50597f5SAlexander Motin * XXX: Values 25% and 50% are completely empirical. 2286d4529f98SAlexander Motin */ 2287f50597f5SAlexander Motin size_t st, su, sl; 228881a253a4SAlexander Motin GET_STACK_USAGE(st, su); 2289f50597f5SAlexander Motin sl = st - su; 22904bd1b557SGleb Smirnoff if ((sl * 4 < st) || ((sl * 2 < st) && 22914bd1b557SGleb Smirnoff ((node->nd_flags & NGF_HI_STACK) || (hook && 22924bd1b557SGleb Smirnoff (hook->hk_flags & HK_HI_STACK))))) 229381a253a4SAlexander Motin queue = 1; 229481a253a4SAlexander Motin #endif 2295f50597f5SAlexander Motin } 229681a253a4SAlexander Motin 2297069154d5SJulian Elischer if (queue) { 2298394cb30aSAlexander Motin /* Put it on the queue for that node*/ 22999852972bSAlexander Motin ng_queue_rw(node, item, rw); 2300f50597f5SAlexander Motin return ((flags & NG_PROGRESS) ? EINPROGRESS : 0); 2301069154d5SJulian Elischer } 2302069154d5SJulian Elischer 2303069154d5SJulian Elischer /* 2304069154d5SJulian Elischer * We already decided how we will be queueud or treated. 2305069154d5SJulian Elischer * Try get the appropriate operating permission. 2306069154d5SJulian Elischer */ 230732b33288SGleb Smirnoff if (rw == NGQRW_R) 23089852972bSAlexander Motin item = ng_acquire_read(node, item); 230932b33288SGleb Smirnoff else 23109852972bSAlexander Motin item = ng_acquire_write(node, item); 23114cf49a43SJulian Elischer 2312f50597f5SAlexander Motin /* Item was queued while trying to get permission. */ 2313f50597f5SAlexander Motin if (item == NULL) 2314f50597f5SAlexander Motin return ((flags & NG_PROGRESS) ? EINPROGRESS : 0); 2315069154d5SJulian Elischer 23165951069aSJulian Elischer NGI_GET_NODE(item, node); /* zaps stored node */ 23175951069aSJulian Elischer 231810e87318SAlexander Motin item->depth++; 231927757487SGleb Smirnoff error = ng_apply_item(node, item, rw); /* drops r/w lock when done */ 2320069154d5SJulian Elischer 2321394cb30aSAlexander Motin /* If something is waiting on queue and ready, schedule it. */ 23229852972bSAlexander Motin ngq = &node->nd_input_queue; 2323394cb30aSAlexander Motin if (QUEUE_ACTIVE(ngq)) { 23242c8dda8dSWojciech A. Koszek NG_QUEUE_LOCK(ngq); 2325394cb30aSAlexander Motin if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) 23269852972bSAlexander Motin ng_worklist_add(node); 23272c8dda8dSWojciech A. Koszek NG_QUEUE_UNLOCK(ngq); 2328394cb30aSAlexander Motin } 2329394cb30aSAlexander Motin 2330394cb30aSAlexander Motin /* 2331394cb30aSAlexander Motin * Node may go away as soon as we remove the reference. 2332394cb30aSAlexander Motin * Whatever we do, DO NOT access the node again! 2333394cb30aSAlexander Motin */ 2334394cb30aSAlexander Motin NG_NODE_UNREF(node); 2335069154d5SJulian Elischer 23361acb27c6SJulian Elischer return (error); 2337e088dd4cSAlexander Motin 2338e088dd4cSAlexander Motin done: 2339f50597f5SAlexander Motin /* If was not sent, apply callback here. */ 234010e87318SAlexander Motin if (item->apply != NULL) { 234110e87318SAlexander Motin if (item->depth == 0 && error != 0) 234210e87318SAlexander Motin item->apply->error = error; 234310e87318SAlexander Motin if (refcount_release(&item->apply->refs)) { 234410e87318SAlexander Motin (*item->apply->apply)(item->apply->context, 234510e87318SAlexander Motin item->apply->error); 234610e87318SAlexander Motin } 234710e87318SAlexander Motin } 2348e72a98f4SAlexander Motin 2349e088dd4cSAlexander Motin NG_FREE_ITEM(item); 2350e088dd4cSAlexander Motin return (error); 2351069154d5SJulian Elischer } 2352069154d5SJulian Elischer 2353069154d5SJulian Elischer /* 2354069154d5SJulian Elischer * We have an item that was possibly queued somewhere. 2355069154d5SJulian Elischer * It should contain all the information needed 2356069154d5SJulian Elischer * to run it on the appropriate node/hook. 2357e088dd4cSAlexander Motin * If there is apply pointer and we own the last reference, call apply(). 23584cf49a43SJulian Elischer */ 235927757487SGleb Smirnoff static int 2360714fb865SGleb Smirnoff ng_apply_item(node_p node, item_p item, int rw) 2361069154d5SJulian Elischer { 2362069154d5SJulian Elischer hook_p hook; 2363069154d5SJulian Elischer ng_rcvdata_t *rcvdata; 2364c4b5eea4SJulian Elischer ng_rcvmsg_t *rcvmsg; 2365e088dd4cSAlexander Motin struct ng_apply_info *apply; 236610e87318SAlexander Motin int error = 0, depth; 2367069154d5SJulian Elischer 2368f50597f5SAlexander Motin /* Node and item are never optional. */ 2369f50597f5SAlexander Motin KASSERT(node != NULL, ("ng_apply_item: node is NULL")); 2370f50597f5SAlexander Motin KASSERT(item != NULL, ("ng_apply_item: item is NULL")); 2371f50597f5SAlexander Motin 23721acb27c6SJulian Elischer NGI_GET_HOOK(item, hook); /* clears stored hook */ 237330400f03SJulian Elischer #ifdef NETGRAPH_DEBUG 2374069154d5SJulian Elischer _ngi_check(item, __FILE__, __LINE__); 2375069154d5SJulian Elischer #endif 23768afe16d5SGleb Smirnoff 23778afe16d5SGleb Smirnoff apply = item->apply; 237810e87318SAlexander Motin depth = item->depth; 23798afe16d5SGleb Smirnoff 23806b795970SJulian Elischer switch (item->el_flags & NGQF_TYPE) { 2381069154d5SJulian Elischer case NGQF_DATA: 2382069154d5SJulian Elischer /* 2383069154d5SJulian Elischer * Check things are still ok as when we were queued. 2384069154d5SJulian Elischer */ 2385f50597f5SAlexander Motin KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL")); 2386f50597f5SAlexander Motin if (NG_HOOK_NOT_VALID(hook) || 2387f50597f5SAlexander Motin NG_NODE_NOT_VALID(node)) { 2388a54a69d7SJulian Elischer error = EIO; 2389069154d5SJulian Elischer NG_FREE_ITEM(item); 2390c4b5eea4SJulian Elischer break; 2391069154d5SJulian Elischer } 2392c4b5eea4SJulian Elischer /* 2393c4b5eea4SJulian Elischer * If no receive method, just silently drop it. 23944bd1b557SGleb Smirnoff * Give preference to the hook over-ride method. 2395c4b5eea4SJulian Elischer */ 23964bd1b557SGleb Smirnoff if ((!(rcvdata = hook->hk_rcvdata)) && 23974bd1b557SGleb Smirnoff (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) { 2398c4b5eea4SJulian Elischer error = 0; 2399c4b5eea4SJulian Elischer NG_FREE_ITEM(item); 2400c4b5eea4SJulian Elischer break; 2401c4b5eea4SJulian Elischer } 2402a54a69d7SJulian Elischer error = (*rcvdata)(hook, item); 2403069154d5SJulian Elischer break; 2404069154d5SJulian Elischer case NGQF_MESG: 2405e72a98f4SAlexander Motin if (hook && NG_HOOK_NOT_VALID(hook)) { 2406069154d5SJulian Elischer /* 2407e72a98f4SAlexander Motin * The hook has been zapped then we can't use it. 2408e72a98f4SAlexander Motin * Immediately drop its reference. 2409069154d5SJulian Elischer * The message may not need it. 2410069154d5SJulian Elischer */ 241130400f03SJulian Elischer NG_HOOK_UNREF(hook); 2412069154d5SJulian Elischer hook = NULL; 2413069154d5SJulian Elischer } 2414069154d5SJulian Elischer /* 2415069154d5SJulian Elischer * Similarly, if the node is a zombie there is 2416069154d5SJulian Elischer * nothing we can do with it, drop everything. 2417069154d5SJulian Elischer */ 241830400f03SJulian Elischer if (NG_NODE_NOT_VALID(node)) { 24196b795970SJulian Elischer TRAP_ERROR(); 2420a54a69d7SJulian Elischer error = EINVAL; 2421069154d5SJulian Elischer NG_FREE_ITEM(item); 2422e72a98f4SAlexander Motin break; 2423e72a98f4SAlexander Motin } 2424069154d5SJulian Elischer /* 2425069154d5SJulian Elischer * Call the appropriate message handler for the object. 2426069154d5SJulian Elischer * It is up to the message handler to free the message. 2427069154d5SJulian Elischer * If it's a generic message, handle it generically, 2428e72a98f4SAlexander Motin * otherwise call the type's message handler (if it exists). 2429069154d5SJulian Elischer * XXX (race). Remember that a queued message may 2430069154d5SJulian Elischer * reference a node or hook that has just been 2431069154d5SJulian Elischer * invalidated. It will exist as the queue code 2432069154d5SJulian Elischer * is holding a reference, but.. 2433069154d5SJulian Elischer */ 2434e72a98f4SAlexander Motin if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) && 2435e72a98f4SAlexander Motin ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) { 2436a54a69d7SJulian Elischer error = ng_generic_msg(node, item, hook); 2437c4b5eea4SJulian Elischer break; 2438c4b5eea4SJulian Elischer } 2439e72a98f4SAlexander Motin if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) && 2440e72a98f4SAlexander Motin (!(rcvmsg = node->nd_type->rcvmsg))) { 24416b795970SJulian Elischer TRAP_ERROR(); 2442a54a69d7SJulian Elischer error = 0; 2443069154d5SJulian Elischer NG_FREE_ITEM(item); 2444c4b5eea4SJulian Elischer break; 2445069154d5SJulian Elischer } 2446a54a69d7SJulian Elischer error = (*rcvmsg)(node, item, hook); 2447069154d5SJulian Elischer break; 24486b795970SJulian Elischer case NGQF_FN: 2449e088dd4cSAlexander Motin case NGQF_FN2: 2450e088dd4cSAlexander Motin /* 245174c9119dSAlexander Motin * In the case of the shutdown message we allow it to hit 2452e088dd4cSAlexander Motin * even if the node is invalid. 2453e088dd4cSAlexander Motin */ 245474c9119dSAlexander Motin if (NG_NODE_NOT_VALID(node) && 245574c9119dSAlexander Motin NGI_FN(item) != &ng_rmnode) { 2456e088dd4cSAlexander Motin TRAP_ERROR(); 2457e088dd4cSAlexander Motin error = EINVAL; 2458e088dd4cSAlexander Motin NG_FREE_ITEM(item); 2459e088dd4cSAlexander Motin break; 2460e088dd4cSAlexander Motin } 246174c9119dSAlexander Motin /* Same is about some internal functions and invalid hook. */ 246274c9119dSAlexander Motin if (hook && NG_HOOK_NOT_VALID(hook) && 246374c9119dSAlexander Motin NGI_FN2(item) != &ng_con_part2 && 246474c9119dSAlexander Motin NGI_FN2(item) != &ng_con_part3 && 246574c9119dSAlexander Motin NGI_FN(item) != &ng_rmhook_part2) { 246674c9119dSAlexander Motin TRAP_ERROR(); 246774c9119dSAlexander Motin error = EINVAL; 246874c9119dSAlexander Motin NG_FREE_ITEM(item); 246974c9119dSAlexander Motin break; 247074c9119dSAlexander Motin } 247174c9119dSAlexander Motin 2472b332b91fSGleb Smirnoff if ((item->el_flags & NGQF_TYPE) == NGQF_FN) { 2473b332b91fSGleb Smirnoff (*NGI_FN(item))(node, hook, NGI_ARG1(item), 2474b332b91fSGleb Smirnoff NGI_ARG2(item)); 2475b332b91fSGleb Smirnoff NG_FREE_ITEM(item); 2476b332b91fSGleb Smirnoff } else /* it is NGQF_FN2 */ 2477e088dd4cSAlexander Motin error = (*NGI_FN2(item))(node, item, hook); 2478e088dd4cSAlexander Motin break; 2479069154d5SJulian Elischer } 2480069154d5SJulian Elischer /* 2481069154d5SJulian Elischer * We held references on some of the resources 2482069154d5SJulian Elischer * that we took from the item. Now that we have 2483069154d5SJulian Elischer * finished doing everything, drop those references. 2484069154d5SJulian Elischer */ 2485e72a98f4SAlexander Motin if (hook) 248630400f03SJulian Elischer NG_HOOK_UNREF(hook); 2487069154d5SJulian Elischer 2488f50597f5SAlexander Motin if (rw == NGQRW_R) 24899852972bSAlexander Motin ng_leave_read(node); 2490f50597f5SAlexander Motin else 24919852972bSAlexander Motin ng_leave_write(node); 24928afe16d5SGleb Smirnoff 24938afe16d5SGleb Smirnoff /* Apply callback. */ 249410e87318SAlexander Motin if (apply != NULL) { 249510e87318SAlexander Motin if (depth == 1 && error != 0) 249610e87318SAlexander Motin apply->error = error; 249710e87318SAlexander Motin if (refcount_release(&apply->refs)) 249810e87318SAlexander Motin (*apply->apply)(apply->context, apply->error); 249910e87318SAlexander Motin } 25008afe16d5SGleb Smirnoff 250127757487SGleb Smirnoff return (error); 2502069154d5SJulian Elischer } 2503069154d5SJulian Elischer 2504069154d5SJulian Elischer /*********************************************************************** 2505069154d5SJulian Elischer * Implement the 'generic' control messages 2506069154d5SJulian Elischer ***********************************************************************/ 2507069154d5SJulian Elischer static int 2508069154d5SJulian Elischer ng_generic_msg(node_p here, item_p item, hook_p lasthook) 25094cf49a43SJulian Elischer { 25104cf49a43SJulian Elischer int error = 0; 2511069154d5SJulian Elischer struct ng_mesg *msg; 2512069154d5SJulian Elischer struct ng_mesg *resp = NULL; 25134cf49a43SJulian Elischer 2514069154d5SJulian Elischer NGI_GET_MSG(item, msg); 25154cf49a43SJulian Elischer if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 25166b795970SJulian Elischer TRAP_ERROR(); 2517069154d5SJulian Elischer error = EINVAL; 2518069154d5SJulian Elischer goto out; 25194cf49a43SJulian Elischer } 25204cf49a43SJulian Elischer switch (msg->header.cmd) { 25214cf49a43SJulian Elischer case NGM_SHUTDOWN: 25221acb27c6SJulian Elischer ng_rmnode(here, NULL, NULL, 0); 25234cf49a43SJulian Elischer break; 25244cf49a43SJulian Elischer case NGM_MKPEER: 25254cf49a43SJulian Elischer { 25264cf49a43SJulian Elischer struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 25274cf49a43SJulian Elischer 25284cf49a43SJulian Elischer if (msg->header.arglen != sizeof(*mkp)) { 25296b795970SJulian Elischer TRAP_ERROR(); 2530069154d5SJulian Elischer error = EINVAL; 2531069154d5SJulian Elischer break; 25324cf49a43SJulian Elischer } 25334cf49a43SJulian Elischer mkp->type[sizeof(mkp->type) - 1] = '\0'; 25344cf49a43SJulian Elischer mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 25354cf49a43SJulian Elischer mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 25364cf49a43SJulian Elischer error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 25374cf49a43SJulian Elischer break; 25384cf49a43SJulian Elischer } 25394cf49a43SJulian Elischer case NGM_CONNECT: 25404cf49a43SJulian Elischer { 25414cf49a43SJulian Elischer struct ngm_connect *const con = 25424cf49a43SJulian Elischer (struct ngm_connect *) msg->data; 25434cf49a43SJulian Elischer node_p node2; 25444cf49a43SJulian Elischer 25454cf49a43SJulian Elischer if (msg->header.arglen != sizeof(*con)) { 25466b795970SJulian Elischer TRAP_ERROR(); 2547069154d5SJulian Elischer error = EINVAL; 2548069154d5SJulian Elischer break; 25494cf49a43SJulian Elischer } 25504cf49a43SJulian Elischer con->path[sizeof(con->path) - 1] = '\0'; 25514cf49a43SJulian Elischer con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 25524cf49a43SJulian Elischer con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 2553069154d5SJulian Elischer /* Don't forget we get a reference.. */ 2554069154d5SJulian Elischer error = ng_path2noderef(here, con->path, &node2, NULL); 25554cf49a43SJulian Elischer if (error) 25564cf49a43SJulian Elischer break; 2557e088dd4cSAlexander Motin error = ng_con_nodes(item, here, con->ourhook, 2558e088dd4cSAlexander Motin node2, con->peerhook); 255930400f03SJulian Elischer NG_NODE_UNREF(node2); 25604cf49a43SJulian Elischer break; 25614cf49a43SJulian Elischer } 25624cf49a43SJulian Elischer case NGM_NAME: 25634cf49a43SJulian Elischer { 25644cf49a43SJulian Elischer struct ngm_name *const nam = (struct ngm_name *) msg->data; 25654cf49a43SJulian Elischer 25664cf49a43SJulian Elischer if (msg->header.arglen != sizeof(*nam)) { 25676b795970SJulian Elischer TRAP_ERROR(); 2568069154d5SJulian Elischer error = EINVAL; 2569069154d5SJulian Elischer break; 25704cf49a43SJulian Elischer } 25714cf49a43SJulian Elischer nam->name[sizeof(nam->name) - 1] = '\0'; 25724cf49a43SJulian Elischer error = ng_name_node(here, nam->name); 25734cf49a43SJulian Elischer break; 25744cf49a43SJulian Elischer } 25754cf49a43SJulian Elischer case NGM_RMHOOK: 25764cf49a43SJulian Elischer { 25774cf49a43SJulian Elischer struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 25784cf49a43SJulian Elischer hook_p hook; 25794cf49a43SJulian Elischer 25804cf49a43SJulian Elischer if (msg->header.arglen != sizeof(*rmh)) { 25816b795970SJulian Elischer TRAP_ERROR(); 2582069154d5SJulian Elischer error = EINVAL; 2583069154d5SJulian Elischer break; 25844cf49a43SJulian Elischer } 25854cf49a43SJulian Elischer rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 2586899e9c4eSArchie Cobbs if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 25874cf49a43SJulian Elischer ng_destroy_hook(hook); 25884cf49a43SJulian Elischer break; 25894cf49a43SJulian Elischer } 25904cf49a43SJulian Elischer case NGM_NODEINFO: 25914cf49a43SJulian Elischer { 25924cf49a43SJulian Elischer struct nodeinfo *ni; 25934cf49a43SJulian Elischer 2594069154d5SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT); 25954cf49a43SJulian Elischer if (resp == NULL) { 25964cf49a43SJulian Elischer error = ENOMEM; 25974cf49a43SJulian Elischer break; 25984cf49a43SJulian Elischer } 25994cf49a43SJulian Elischer 26004cf49a43SJulian Elischer /* Fill in node info */ 2601069154d5SJulian Elischer ni = (struct nodeinfo *) resp->data; 260230400f03SJulian Elischer if (NG_NODE_HAS_NAME(here)) 260387e2c66aSHartmut Brandt strcpy(ni->name, NG_NODE_NAME(here)); 260487e2c66aSHartmut Brandt strcpy(ni->type, here->nd_type->name); 2605dc90cad9SJulian Elischer ni->id = ng_node2ID(here); 260630400f03SJulian Elischer ni->hooks = here->nd_numhooks; 26074cf49a43SJulian Elischer break; 26084cf49a43SJulian Elischer } 26094cf49a43SJulian Elischer case NGM_LISTHOOKS: 26104cf49a43SJulian Elischer { 261130400f03SJulian Elischer const int nhooks = here->nd_numhooks; 26124cf49a43SJulian Elischer struct hooklist *hl; 26134cf49a43SJulian Elischer struct nodeinfo *ni; 26144cf49a43SJulian Elischer hook_p hook; 26154cf49a43SJulian Elischer 26164cf49a43SJulian Elischer /* Get response struct */ 26174bd1b557SGleb Smirnoff NG_MKRESPONSE(resp, msg, sizeof(*hl) + 26184bd1b557SGleb Smirnoff (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 2619069154d5SJulian Elischer if (resp == NULL) { 26204cf49a43SJulian Elischer error = ENOMEM; 26214cf49a43SJulian Elischer break; 26224cf49a43SJulian Elischer } 2623069154d5SJulian Elischer hl = (struct hooklist *) resp->data; 26244cf49a43SJulian Elischer ni = &hl->nodeinfo; 26254cf49a43SJulian Elischer 26264cf49a43SJulian Elischer /* Fill in node info */ 262730400f03SJulian Elischer if (NG_NODE_HAS_NAME(here)) 262887e2c66aSHartmut Brandt strcpy(ni->name, NG_NODE_NAME(here)); 262987e2c66aSHartmut Brandt strcpy(ni->type, here->nd_type->name); 2630dc90cad9SJulian Elischer ni->id = ng_node2ID(here); 26314cf49a43SJulian Elischer 26324cf49a43SJulian Elischer /* Cycle through the linked list of hooks */ 26334cf49a43SJulian Elischer ni->hooks = 0; 263430400f03SJulian Elischer LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) { 26354cf49a43SJulian Elischer struct linkinfo *const link = &hl->link[ni->hooks]; 26364cf49a43SJulian Elischer 26374cf49a43SJulian Elischer if (ni->hooks >= nhooks) { 26384cf49a43SJulian Elischer log(LOG_ERR, "%s: number of %s changed\n", 26396e551fb6SDavid E. O'Brien __func__, "hooks"); 26404cf49a43SJulian Elischer break; 26414cf49a43SJulian Elischer } 264230400f03SJulian Elischer if (NG_HOOK_NOT_VALID(hook)) 26434cf49a43SJulian Elischer continue; 264487e2c66aSHartmut Brandt strcpy(link->ourhook, NG_HOOK_NAME(hook)); 264587e2c66aSHartmut Brandt strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook)); 264630400f03SJulian Elischer if (NG_PEER_NODE_NAME(hook)[0] != '\0') 264787e2c66aSHartmut Brandt strcpy(link->nodeinfo.name, 264887e2c66aSHartmut Brandt NG_PEER_NODE_NAME(hook)); 264987e2c66aSHartmut Brandt strcpy(link->nodeinfo.type, 265087e2c66aSHartmut Brandt NG_PEER_NODE(hook)->nd_type->name); 265130400f03SJulian Elischer link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook)); 265230400f03SJulian Elischer link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks; 26534cf49a43SJulian Elischer ni->hooks++; 26544cf49a43SJulian Elischer } 26554cf49a43SJulian Elischer break; 26564cf49a43SJulian Elischer } 26574cf49a43SJulian Elischer 26584cf49a43SJulian Elischer case NGM_LISTNODES: 26594cf49a43SJulian Elischer { 26604cf49a43SJulian Elischer struct namelist *nl; 26614cf49a43SJulian Elischer node_p node; 2662687adb70SGleb Smirnoff int i; 2663687adb70SGleb Smirnoff 2664687adb70SGleb Smirnoff IDHASH_RLOCK(); 2665687adb70SGleb Smirnoff /* Get response struct. */ 2666687adb70SGleb Smirnoff NG_MKRESPONSE(resp, msg, sizeof(*nl) + 2667687adb70SGleb Smirnoff (V_ng_nodes * sizeof(struct nodeinfo)), M_NOWAIT | M_ZERO); 2668687adb70SGleb Smirnoff if (resp == NULL) { 2669687adb70SGleb Smirnoff IDHASH_RUNLOCK(); 2670687adb70SGleb Smirnoff error = ENOMEM; 2671687adb70SGleb Smirnoff break; 2672687adb70SGleb Smirnoff } 2673687adb70SGleb Smirnoff nl = (struct namelist *) resp->data; 2674687adb70SGleb Smirnoff 2675687adb70SGleb Smirnoff /* Cycle through the lists of nodes. */ 2676687adb70SGleb Smirnoff nl->numnames = 0; 2677687adb70SGleb Smirnoff for (i = 0; i <= V_ng_ID_hmask; i++) { 2678687adb70SGleb Smirnoff LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) { 2679687adb70SGleb Smirnoff struct nodeinfo *const np = 2680687adb70SGleb Smirnoff &nl->nodeinfo[nl->numnames]; 2681687adb70SGleb Smirnoff 2682687adb70SGleb Smirnoff if (NG_NODE_NOT_VALID(node)) 2683687adb70SGleb Smirnoff continue; 2684687adb70SGleb Smirnoff if (NG_NODE_HAS_NAME(node)) 2685687adb70SGleb Smirnoff strcpy(np->name, NG_NODE_NAME(node)); 2686687adb70SGleb Smirnoff strcpy(np->type, node->nd_type->name); 2687687adb70SGleb Smirnoff np->id = ng_node2ID(node); 2688687adb70SGleb Smirnoff np->hooks = node->nd_numhooks; 2689687adb70SGleb Smirnoff KASSERT(nl->numnames < V_ng_nodes, 2690687adb70SGleb Smirnoff ("%s: no space", __func__)); 2691687adb70SGleb Smirnoff nl->numnames++; 2692687adb70SGleb Smirnoff } 2693687adb70SGleb Smirnoff } 2694687adb70SGleb Smirnoff IDHASH_RUNLOCK(); 2695687adb70SGleb Smirnoff break; 2696687adb70SGleb Smirnoff } 2697687adb70SGleb Smirnoff case NGM_LISTNAMES: 2698687adb70SGleb Smirnoff { 2699687adb70SGleb Smirnoff struct namelist *nl; 2700687adb70SGleb Smirnoff node_p node; 2701687adb70SGleb Smirnoff int i; 27024cf49a43SJulian Elischer 2703c4282b74SGleb Smirnoff NAMEHASH_RLOCK(); 2704687adb70SGleb Smirnoff /* Get response struct. */ 27054bd1b557SGleb Smirnoff NG_MKRESPONSE(resp, msg, sizeof(*nl) + 2706687adb70SGleb Smirnoff (V_ng_named_nodes * sizeof(struct nodeinfo)), M_NOWAIT); 2707069154d5SJulian Elischer if (resp == NULL) { 2708c4282b74SGleb Smirnoff NAMEHASH_RUNLOCK(); 27094cf49a43SJulian Elischer error = ENOMEM; 27104cf49a43SJulian Elischer break; 27114cf49a43SJulian Elischer } 2712069154d5SJulian Elischer nl = (struct namelist *) resp->data; 27134cf49a43SJulian Elischer 2714687adb70SGleb Smirnoff /* Cycle through the lists of nodes. */ 27154cf49a43SJulian Elischer nl->numnames = 0; 2716687adb70SGleb Smirnoff for (i = 0; i <= V_ng_name_hmask; i++) { 2717603724d3SBjoern A. Zeeb LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) { 2718cfea3f85SAlexander Motin struct nodeinfo *const np = 2719cfea3f85SAlexander Motin &nl->nodeinfo[nl->numnames]; 27204cf49a43SJulian Elischer 2721b96baf0aSGleb Smirnoff if (NG_NODE_NOT_VALID(node)) 2722b96baf0aSGleb Smirnoff continue; 272387e2c66aSHartmut Brandt strcpy(np->name, NG_NODE_NAME(node)); 272487e2c66aSHartmut Brandt strcpy(np->type, node->nd_type->name); 2725dc90cad9SJulian Elischer np->id = ng_node2ID(node); 272630400f03SJulian Elischer np->hooks = node->nd_numhooks; 2727687adb70SGleb Smirnoff KASSERT(nl->numnames < V_ng_named_nodes, 2728687adb70SGleb Smirnoff ("%s: no space", __func__)); 27294cf49a43SJulian Elischer nl->numnames++; 27304cf49a43SJulian Elischer } 2731cfea3f85SAlexander Motin } 2732c4282b74SGleb Smirnoff NAMEHASH_RUNLOCK(); 27334cf49a43SJulian Elischer break; 27344cf49a43SJulian Elischer } 27354cf49a43SJulian Elischer 27364cf49a43SJulian Elischer case NGM_LISTTYPES: 27374cf49a43SJulian Elischer { 27384cf49a43SJulian Elischer struct typelist *tl; 27394cf49a43SJulian Elischer struct ng_type *type; 27404cf49a43SJulian Elischer int num = 0; 27414cf49a43SJulian Elischer 2742c4282b74SGleb Smirnoff TYPELIST_RLOCK(); 27434cf49a43SJulian Elischer /* Count number of types */ 2744c4282b74SGleb Smirnoff LIST_FOREACH(type, &ng_typelist, types) 27454cf49a43SJulian Elischer num++; 27464cf49a43SJulian Elischer 27474cf49a43SJulian Elischer /* Get response struct */ 27484bd1b557SGleb Smirnoff NG_MKRESPONSE(resp, msg, sizeof(*tl) + 27494bd1b557SGleb Smirnoff (num * sizeof(struct typeinfo)), M_NOWAIT); 2750069154d5SJulian Elischer if (resp == NULL) { 2751c4282b74SGleb Smirnoff TYPELIST_RUNLOCK(); 27524cf49a43SJulian Elischer error = ENOMEM; 27534cf49a43SJulian Elischer break; 27544cf49a43SJulian Elischer } 2755069154d5SJulian Elischer tl = (struct typelist *) resp->data; 27564cf49a43SJulian Elischer 27574cf49a43SJulian Elischer /* Cycle through the linked list of types */ 27584cf49a43SJulian Elischer tl->numtypes = 0; 2759069154d5SJulian Elischer LIST_FOREACH(type, &ng_typelist, types) { 27604cf49a43SJulian Elischer struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 27614cf49a43SJulian Elischer 276287e2c66aSHartmut Brandt strcpy(tp->type_name, type->name); 2763c73b94a2SJulian Elischer tp->numnodes = type->refs - 1; /* don't count list */ 2764c4282b74SGleb Smirnoff KASSERT(tl->numtypes < num, ("%s: no space", __func__)); 27654cf49a43SJulian Elischer tl->numtypes++; 27664cf49a43SJulian Elischer } 2767c4282b74SGleb Smirnoff TYPELIST_RUNLOCK(); 27684cf49a43SJulian Elischer break; 27694cf49a43SJulian Elischer } 27704cf49a43SJulian Elischer 2771f8307e12SArchie Cobbs case NGM_BINARY2ASCII: 2772f8307e12SArchie Cobbs { 27737133ac27SArchie Cobbs int bufSize = 20 * 1024; /* XXX hard coded constant */ 2774f8307e12SArchie Cobbs const struct ng_parse_type *argstype; 2775f8307e12SArchie Cobbs const struct ng_cmdlist *c; 2776069154d5SJulian Elischer struct ng_mesg *binary, *ascii; 2777f8307e12SArchie Cobbs 2778f8307e12SArchie Cobbs /* Data area must contain a valid netgraph message */ 2779f8307e12SArchie Cobbs binary = (struct ng_mesg *)msg->data; 27804c9b5910SGleb Smirnoff if (msg->header.arglen < sizeof(struct ng_mesg) || 27814c9b5910SGleb Smirnoff (msg->header.arglen - sizeof(struct ng_mesg) < 27824c9b5910SGleb Smirnoff binary->header.arglen)) { 27836b795970SJulian Elischer TRAP_ERROR(); 2784f8307e12SArchie Cobbs error = EINVAL; 2785f8307e12SArchie Cobbs break; 2786f8307e12SArchie Cobbs } 2787f8307e12SArchie Cobbs 2788f8307e12SArchie Cobbs /* Get a response message with lots of room */ 2789069154d5SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 2790069154d5SJulian Elischer if (resp == NULL) { 2791f8307e12SArchie Cobbs error = ENOMEM; 2792f8307e12SArchie Cobbs break; 2793f8307e12SArchie Cobbs } 2794069154d5SJulian Elischer ascii = (struct ng_mesg *)resp->data; 2795f8307e12SArchie Cobbs 2796f8307e12SArchie Cobbs /* Copy binary message header to response message payload */ 2797f8307e12SArchie Cobbs bcopy(binary, ascii, sizeof(*binary)); 2798f8307e12SArchie Cobbs 2799f8307e12SArchie Cobbs /* Find command by matching typecookie and command number */ 28004bd1b557SGleb Smirnoff for (c = here->nd_type->cmdlist; c != NULL && c->name != NULL; 28014bd1b557SGleb Smirnoff c++) { 28024bd1b557SGleb Smirnoff if (binary->header.typecookie == c->cookie && 28034bd1b557SGleb Smirnoff binary->header.cmd == c->cmd) 2804f8307e12SArchie Cobbs break; 2805f8307e12SArchie Cobbs } 2806f8307e12SArchie Cobbs if (c == NULL || c->name == NULL) { 2807f8307e12SArchie Cobbs for (c = ng_generic_cmds; c->name != NULL; c++) { 28084bd1b557SGleb Smirnoff if (binary->header.typecookie == c->cookie && 28094bd1b557SGleb Smirnoff binary->header.cmd == c->cmd) 2810f8307e12SArchie Cobbs break; 2811f8307e12SArchie Cobbs } 2812f8307e12SArchie Cobbs if (c->name == NULL) { 2813069154d5SJulian Elischer NG_FREE_MSG(resp); 2814f8307e12SArchie Cobbs error = ENOSYS; 2815f8307e12SArchie Cobbs break; 2816f8307e12SArchie Cobbs } 2817f8307e12SArchie Cobbs } 2818f8307e12SArchie Cobbs 2819f8307e12SArchie Cobbs /* Convert command name to ASCII */ 2820f8307e12SArchie Cobbs snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 2821f8307e12SArchie Cobbs "%s", c->name); 2822f8307e12SArchie Cobbs 2823f8307e12SArchie Cobbs /* Convert command arguments to ASCII */ 2824f8307e12SArchie Cobbs argstype = (binary->header.flags & NGF_RESP) ? 2825f8307e12SArchie Cobbs c->respType : c->mesgType; 282670de87f2SJulian Elischer if (argstype == NULL) { 2827f8307e12SArchie Cobbs *ascii->data = '\0'; 282870de87f2SJulian Elischer } else { 2829f8307e12SArchie Cobbs if ((error = ng_unparse(argstype, 2830f8307e12SArchie Cobbs (u_char *)binary->data, 2831f8307e12SArchie Cobbs ascii->data, bufSize)) != 0) { 2832069154d5SJulian Elischer NG_FREE_MSG(resp); 2833f8307e12SArchie Cobbs break; 2834f8307e12SArchie Cobbs } 2835f8307e12SArchie Cobbs } 2836f8307e12SArchie Cobbs 2837f8307e12SArchie Cobbs /* Return the result as struct ng_mesg plus ASCII string */ 2838f8307e12SArchie Cobbs bufSize = strlen(ascii->data) + 1; 2839f8307e12SArchie Cobbs ascii->header.arglen = bufSize; 2840069154d5SJulian Elischer resp->header.arglen = sizeof(*ascii) + bufSize; 2841f8307e12SArchie Cobbs break; 2842f8307e12SArchie Cobbs } 2843f8307e12SArchie Cobbs 2844f8307e12SArchie Cobbs case NGM_ASCII2BINARY: 2845f8307e12SArchie Cobbs { 284698a5a343SMarko Zec int bufSize = 20 * 1024; /* XXX hard coded constant */ 2847f8307e12SArchie Cobbs const struct ng_cmdlist *c; 2848f8307e12SArchie Cobbs const struct ng_parse_type *argstype; 2849069154d5SJulian Elischer struct ng_mesg *ascii, *binary; 285052ec4a03SArchie Cobbs int off = 0; 2851f8307e12SArchie Cobbs 2852f8307e12SArchie Cobbs /* Data area must contain at least a struct ng_mesg + '\0' */ 2853f8307e12SArchie Cobbs ascii = (struct ng_mesg *)msg->data; 28544c9b5910SGleb Smirnoff if ((msg->header.arglen < sizeof(*ascii) + 1) || 28554c9b5910SGleb Smirnoff (ascii->header.arglen < 1) || 28564c9b5910SGleb Smirnoff (msg->header.arglen < sizeof(*ascii) + 28574c9b5910SGleb Smirnoff ascii->header.arglen)) { 28586b795970SJulian Elischer TRAP_ERROR(); 2859f8307e12SArchie Cobbs error = EINVAL; 2860f8307e12SArchie Cobbs break; 2861f8307e12SArchie Cobbs } 2862f8307e12SArchie Cobbs ascii->data[ascii->header.arglen - 1] = '\0'; 2863f8307e12SArchie Cobbs 2864f8307e12SArchie Cobbs /* Get a response message with lots of room */ 2865069154d5SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 2866069154d5SJulian Elischer if (resp == NULL) { 2867f8307e12SArchie Cobbs error = ENOMEM; 2868f8307e12SArchie Cobbs break; 2869f8307e12SArchie Cobbs } 2870069154d5SJulian Elischer binary = (struct ng_mesg *)resp->data; 2871f8307e12SArchie Cobbs 2872f8307e12SArchie Cobbs /* Copy ASCII message header to response message payload */ 2873f8307e12SArchie Cobbs bcopy(ascii, binary, sizeof(*ascii)); 2874f8307e12SArchie Cobbs 2875f8307e12SArchie Cobbs /* Find command by matching ASCII command string */ 287630400f03SJulian Elischer for (c = here->nd_type->cmdlist; 2877f8307e12SArchie Cobbs c != NULL && c->name != NULL; c++) { 2878f8307e12SArchie Cobbs if (strcmp(ascii->header.cmdstr, c->name) == 0) 2879f8307e12SArchie Cobbs break; 2880f8307e12SArchie Cobbs } 2881f8307e12SArchie Cobbs if (c == NULL || c->name == NULL) { 2882f8307e12SArchie Cobbs for (c = ng_generic_cmds; c->name != NULL; c++) { 2883f8307e12SArchie Cobbs if (strcmp(ascii->header.cmdstr, c->name) == 0) 2884f8307e12SArchie Cobbs break; 2885f8307e12SArchie Cobbs } 2886f8307e12SArchie Cobbs if (c->name == NULL) { 2887069154d5SJulian Elischer NG_FREE_MSG(resp); 2888f8307e12SArchie Cobbs error = ENOSYS; 2889f8307e12SArchie Cobbs break; 2890f8307e12SArchie Cobbs } 2891f8307e12SArchie Cobbs } 2892f8307e12SArchie Cobbs 2893f8307e12SArchie Cobbs /* Convert command name to binary */ 2894f8307e12SArchie Cobbs binary->header.cmd = c->cmd; 2895f8307e12SArchie Cobbs binary->header.typecookie = c->cookie; 2896f8307e12SArchie Cobbs 2897f8307e12SArchie Cobbs /* Convert command arguments to binary */ 2898f8307e12SArchie Cobbs argstype = (binary->header.flags & NGF_RESP) ? 2899f8307e12SArchie Cobbs c->respType : c->mesgType; 290070de87f2SJulian Elischer if (argstype == NULL) { 2901f8307e12SArchie Cobbs bufSize = 0; 290270de87f2SJulian Elischer } else { 29034bd1b557SGleb Smirnoff if ((error = ng_parse(argstype, ascii->data, &off, 29044bd1b557SGleb Smirnoff (u_char *)binary->data, &bufSize)) != 0) { 2905069154d5SJulian Elischer NG_FREE_MSG(resp); 2906f8307e12SArchie Cobbs break; 2907f8307e12SArchie Cobbs } 2908f8307e12SArchie Cobbs } 2909f8307e12SArchie Cobbs 2910f8307e12SArchie Cobbs /* Return the result */ 2911f8307e12SArchie Cobbs binary->header.arglen = bufSize; 2912069154d5SJulian Elischer resp->header.arglen = sizeof(*binary) + bufSize; 2913f8307e12SArchie Cobbs break; 2914f8307e12SArchie Cobbs } 2915f8307e12SArchie Cobbs 29167095e097SPoul-Henning Kamp case NGM_TEXT_CONFIG: 29174cf49a43SJulian Elischer case NGM_TEXT_STATUS: 29184cf49a43SJulian Elischer /* 29194cf49a43SJulian Elischer * This one is tricky as it passes the command down to the 29204cf49a43SJulian Elischer * actual node, even though it is a generic type command. 2921069154d5SJulian Elischer * This means we must assume that the item/msg is already freed 29224cf49a43SJulian Elischer * when control passes back to us. 29234cf49a43SJulian Elischer */ 292430400f03SJulian Elischer if (here->nd_type->rcvmsg != NULL) { 2925069154d5SJulian Elischer NGI_MSG(item) = msg; /* put it back as we found it */ 292630400f03SJulian Elischer return((*here->nd_type->rcvmsg)(here, item, lasthook)); 29274cf49a43SJulian Elischer } 29284cf49a43SJulian Elischer /* Fall through if rcvmsg not supported */ 29294cf49a43SJulian Elischer default: 29306b795970SJulian Elischer TRAP_ERROR(); 29314cf49a43SJulian Elischer error = EINVAL; 29324cf49a43SJulian Elischer } 2933069154d5SJulian Elischer /* 2934069154d5SJulian Elischer * Sometimes a generic message may be statically allocated 29354bd1b557SGleb Smirnoff * to avoid problems with allocating when in tight memory situations. 2936069154d5SJulian Elischer * Don't free it if it is so. 2937069154d5SJulian Elischer * I break them appart here, because erros may cause a free if the item 2938069154d5SJulian Elischer * in which case we'd be doing it twice. 2939069154d5SJulian Elischer * they are kept together above, to simplify freeing. 2940069154d5SJulian Elischer */ 2941069154d5SJulian Elischer out: 2942069154d5SJulian Elischer NG_RESPOND_MSG(error, here, item, resp); 2943069154d5SJulian Elischer NG_FREE_MSG(msg); 29444cf49a43SJulian Elischer return (error); 29454cf49a43SJulian Elischer } 29464cf49a43SJulian Elischer 29474cf49a43SJulian Elischer /************************************************************************ 29488253c060SGleb Smirnoff Queue element get/free routines 29498253c060SGleb Smirnoff ************************************************************************/ 29508253c060SGleb Smirnoff 29518253c060SGleb Smirnoff uma_zone_t ng_qzone; 29526aa6d011SAlexander Motin uma_zone_t ng_qdzone; 2953f2fbb838SAlexander Motin static int numthreads = 0; /* number of queue threads */ 2954ed75521fSAlexander Motin static int maxalloc = 4096;/* limit the damage of a leak */ 2955ed75521fSAlexander Motin static int maxdata = 512; /* limit the damage of a DoS */ 29568253c060SGleb Smirnoff 2957f2fbb838SAlexander Motin TUNABLE_INT("net.graph.threads", &numthreads); 2958f2fbb838SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads, 2959f2fbb838SAlexander Motin 0, "Number of queue processing threads"); 29608253c060SGleb Smirnoff TUNABLE_INT("net.graph.maxalloc", &maxalloc); 29618253c060SGleb Smirnoff SYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc, 29626aa6d011SAlexander Motin 0, "Maximum number of non-data queue items to allocate"); 2963ed75521fSAlexander Motin TUNABLE_INT("net.graph.maxdata", &maxdata); 29646aa6d011SAlexander Motin SYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata, 29656aa6d011SAlexander Motin 0, "Maximum number of data queue items to allocate"); 29668253c060SGleb Smirnoff 29678253c060SGleb Smirnoff #ifdef NETGRAPH_DEBUG 29688253c060SGleb Smirnoff static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist); 29698253c060SGleb Smirnoff static int allocated; /* number of items malloc'd */ 29708253c060SGleb Smirnoff #endif 29718253c060SGleb Smirnoff 29728253c060SGleb Smirnoff /* 29738253c060SGleb Smirnoff * Get a queue entry. 29748253c060SGleb Smirnoff * This is usually called when a packet first enters netgraph. 29758253c060SGleb Smirnoff * By definition, this is usually from an interrupt, or from a user. 29768253c060SGleb Smirnoff * Users are not so important, but try be quick for the times that it's 29778253c060SGleb Smirnoff * an interrupt. 29788253c060SGleb Smirnoff */ 29798253c060SGleb Smirnoff static __inline item_p 29806aa6d011SAlexander Motin ng_alloc_item(int type, int flags) 29818253c060SGleb Smirnoff { 29826aa6d011SAlexander Motin item_p item; 29838253c060SGleb Smirnoff 29846aa6d011SAlexander Motin KASSERT(((type & ~NGQF_TYPE) == 0), 29856aa6d011SAlexander Motin ("%s: incorrect item type: %d", __func__, type)); 298642282202SGleb Smirnoff 29876aa6d011SAlexander Motin item = uma_zalloc((type == NGQF_DATA) ? ng_qdzone : ng_qzone, 29886aa6d011SAlexander Motin ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO); 29898253c060SGleb Smirnoff 29908253c060SGleb Smirnoff if (item) { 29916aa6d011SAlexander Motin item->el_flags = type; 29926aa6d011SAlexander Motin #ifdef NETGRAPH_DEBUG 29938253c060SGleb Smirnoff mtx_lock(&ngq_mtx); 29948253c060SGleb Smirnoff TAILQ_INSERT_TAIL(&ng_itemlist, item, all); 29958253c060SGleb Smirnoff allocated++; 29968253c060SGleb Smirnoff mtx_unlock(&ngq_mtx); 29978253c060SGleb Smirnoff #endif 29986aa6d011SAlexander Motin } 29998253c060SGleb Smirnoff 30008253c060SGleb Smirnoff return (item); 30018253c060SGleb Smirnoff } 30028253c060SGleb Smirnoff 30038253c060SGleb Smirnoff /* 30048253c060SGleb Smirnoff * Release a queue entry 30058253c060SGleb Smirnoff */ 30068253c060SGleb Smirnoff void 30078253c060SGleb Smirnoff ng_free_item(item_p item) 30088253c060SGleb Smirnoff { 30098253c060SGleb Smirnoff /* 30108253c060SGleb Smirnoff * The item may hold resources on it's own. We need to free 30118253c060SGleb Smirnoff * these before we can free the item. What they are depends upon 30128253c060SGleb Smirnoff * what kind of item it is. it is important that nodes zero 30138253c060SGleb Smirnoff * out pointers to resources that they remove from the item 30148253c060SGleb Smirnoff * or we release them again here. 30158253c060SGleb Smirnoff */ 30168253c060SGleb Smirnoff switch (item->el_flags & NGQF_TYPE) { 30178253c060SGleb Smirnoff case NGQF_DATA: 30188253c060SGleb Smirnoff /* If we have an mbuf still attached.. */ 30198253c060SGleb Smirnoff NG_FREE_M(_NGI_M(item)); 30208253c060SGleb Smirnoff break; 30218253c060SGleb Smirnoff case NGQF_MESG: 30228253c060SGleb Smirnoff _NGI_RETADDR(item) = 0; 30238253c060SGleb Smirnoff NG_FREE_MSG(_NGI_MSG(item)); 30248253c060SGleb Smirnoff break; 30258253c060SGleb Smirnoff case NGQF_FN: 3026e088dd4cSAlexander Motin case NGQF_FN2: 30278253c060SGleb Smirnoff /* nothing to free really, */ 30288253c060SGleb Smirnoff _NGI_FN(item) = NULL; 30298253c060SGleb Smirnoff _NGI_ARG1(item) = NULL; 30308253c060SGleb Smirnoff _NGI_ARG2(item) = 0; 30318253c060SGleb Smirnoff break; 30328253c060SGleb Smirnoff } 30338253c060SGleb Smirnoff /* If we still have a node or hook referenced... */ 30348253c060SGleb Smirnoff _NGI_CLR_NODE(item); 30358253c060SGleb Smirnoff _NGI_CLR_HOOK(item); 30368253c060SGleb Smirnoff 30378253c060SGleb Smirnoff #ifdef NETGRAPH_DEBUG 30388253c060SGleb Smirnoff mtx_lock(&ngq_mtx); 30398253c060SGleb Smirnoff TAILQ_REMOVE(&ng_itemlist, item, all); 30408253c060SGleb Smirnoff allocated--; 30418253c060SGleb Smirnoff mtx_unlock(&ngq_mtx); 30428253c060SGleb Smirnoff #endif 30436aa6d011SAlexander Motin uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA) ? 30446aa6d011SAlexander Motin ng_qdzone : ng_qzone, item); 30456aa6d011SAlexander Motin } 30466aa6d011SAlexander Motin 30476aa6d011SAlexander Motin /* 30486aa6d011SAlexander Motin * Change type of the queue entry. 30496aa6d011SAlexander Motin * Possibly reallocates it from another UMA zone. 30506aa6d011SAlexander Motin */ 30516aa6d011SAlexander Motin static __inline item_p 30526aa6d011SAlexander Motin ng_realloc_item(item_p pitem, int type, int flags) 30536aa6d011SAlexander Motin { 30546aa6d011SAlexander Motin item_p item; 30556aa6d011SAlexander Motin int from, to; 30566aa6d011SAlexander Motin 30576aa6d011SAlexander Motin KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__)); 30586aa6d011SAlexander Motin KASSERT(((type & ~NGQF_TYPE) == 0), 30596aa6d011SAlexander Motin ("%s: incorrect item type: %d", __func__, type)); 30606aa6d011SAlexander Motin 30616aa6d011SAlexander Motin from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA); 30626aa6d011SAlexander Motin to = (type == NGQF_DATA); 30636aa6d011SAlexander Motin if (from != to) { 30646aa6d011SAlexander Motin /* If reallocation is required do it and copy item. */ 30656aa6d011SAlexander Motin if ((item = ng_alloc_item(type, flags)) == NULL) { 30666aa6d011SAlexander Motin ng_free_item(pitem); 30676aa6d011SAlexander Motin return (NULL); 30686aa6d011SAlexander Motin } 30696aa6d011SAlexander Motin *item = *pitem; 30706aa6d011SAlexander Motin ng_free_item(pitem); 30716aa6d011SAlexander Motin } else 30726aa6d011SAlexander Motin item = pitem; 30736aa6d011SAlexander Motin item->el_flags = (item->el_flags & ~NGQF_TYPE) | type; 30746aa6d011SAlexander Motin 30756aa6d011SAlexander Motin return (item); 30768253c060SGleb Smirnoff } 30778253c060SGleb Smirnoff 30788253c060SGleb Smirnoff /************************************************************************ 30794cf49a43SJulian Elischer Module routines 30804cf49a43SJulian Elischer ************************************************************************/ 30814cf49a43SJulian Elischer 30824cf49a43SJulian Elischer /* 30834cf49a43SJulian Elischer * Handle the loading/unloading of a netgraph node type module 30844cf49a43SJulian Elischer */ 30854cf49a43SJulian Elischer int 30864cf49a43SJulian Elischer ng_mod_event(module_t mod, int event, void *data) 30874cf49a43SJulian Elischer { 30884cf49a43SJulian Elischer struct ng_type *const type = data; 30894bd1b557SGleb Smirnoff int error = 0; 30904cf49a43SJulian Elischer 30914cf49a43SJulian Elischer switch (event) { 30924cf49a43SJulian Elischer case MOD_LOAD: 30934cf49a43SJulian Elischer 30944cf49a43SJulian Elischer /* Register new netgraph node type */ 30954bd1b557SGleb Smirnoff if ((error = ng_newtype(type)) != 0) 30964cf49a43SJulian Elischer break; 30974cf49a43SJulian Elischer 30984cf49a43SJulian Elischer /* Call type specific code */ 30994cf49a43SJulian Elischer if (type->mod_event != NULL) 3100069154d5SJulian Elischer if ((error = (*type->mod_event)(mod, event, data))) { 3101c4282b74SGleb Smirnoff TYPELIST_WLOCK(); 3102c73b94a2SJulian Elischer type->refs--; /* undo it */ 31034cf49a43SJulian Elischer LIST_REMOVE(type, types); 3104c4282b74SGleb Smirnoff TYPELIST_WUNLOCK(); 3105069154d5SJulian Elischer } 31064cf49a43SJulian Elischer break; 31074cf49a43SJulian Elischer 31084cf49a43SJulian Elischer case MOD_UNLOAD: 3109c73b94a2SJulian Elischer if (type->refs > 1) { /* make sure no nodes exist! */ 31104cf49a43SJulian Elischer error = EBUSY; 3111c73b94a2SJulian Elischer } else { 31124bd1b557SGleb Smirnoff if (type->refs == 0) /* failed load, nothing to undo */ 3113c73b94a2SJulian Elischer break; 31144cf49a43SJulian Elischer if (type->mod_event != NULL) { /* check with type */ 31154cf49a43SJulian Elischer error = (*type->mod_event)(mod, event, data); 31164bd1b557SGleb Smirnoff if (error != 0) /* type refuses.. */ 31174cf49a43SJulian Elischer break; 31184cf49a43SJulian Elischer } 3119c4282b74SGleb Smirnoff TYPELIST_WLOCK(); 31204cf49a43SJulian Elischer LIST_REMOVE(type, types); 3121c4282b74SGleb Smirnoff TYPELIST_WUNLOCK(); 31224cf49a43SJulian Elischer } 31234cf49a43SJulian Elischer break; 31244cf49a43SJulian Elischer 31254cf49a43SJulian Elischer default: 31264cf49a43SJulian Elischer if (type->mod_event != NULL) 31274cf49a43SJulian Elischer error = (*type->mod_event)(mod, event, data); 31284cf49a43SJulian Elischer else 31293e019deaSPoul-Henning Kamp error = EOPNOTSUPP; /* XXX ? */ 31304cf49a43SJulian Elischer break; 31314cf49a43SJulian Elischer } 31324cf49a43SJulian Elischer return (error); 31334cf49a43SJulian Elischer } 31344cf49a43SJulian Elischer 3135687adb70SGleb Smirnoff static void 3136687adb70SGleb Smirnoff vnet_netgraph_init(const void *unused __unused) 3137687adb70SGleb Smirnoff { 3138687adb70SGleb Smirnoff 3139687adb70SGleb Smirnoff /* We start with small hashes, but they can grow. */ 3140687adb70SGleb Smirnoff V_ng_ID_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_ID_hmask); 3141687adb70SGleb Smirnoff V_ng_name_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_name_hmask); 3142687adb70SGleb Smirnoff } 3143687adb70SGleb Smirnoff VNET_SYSINIT(vnet_netgraph_init, SI_SUB_NETGRAPH, SI_ORDER_FIRST, 3144687adb70SGleb Smirnoff vnet_netgraph_init, NULL); 3145687adb70SGleb Smirnoff 3146eddfbb76SRobert Watson #ifdef VIMAGE 3147d0728d71SRobert Watson static void 3148d0728d71SRobert Watson vnet_netgraph_uninit(const void *unused __unused) 3149bc29160dSMarko Zec { 3150a3f93b72SMarko Zec node_p node = NULL, last_killed = NULL; 3151a3f93b72SMarko Zec int i; 3152bc29160dSMarko Zec 3153a3f93b72SMarko Zec do { 3154a3f93b72SMarko Zec /* Find a node to kill */ 3155687adb70SGleb Smirnoff IDHASH_RLOCK(); 3156687adb70SGleb Smirnoff for (i = 0; i <= V_ng_ID_hmask; i++) { 3157687adb70SGleb Smirnoff LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) { 3158a3f93b72SMarko Zec if (node != &ng_deadnode) { 3159a3f93b72SMarko Zec NG_NODE_REF(node); 3160a3f93b72SMarko Zec break; 3161a3f93b72SMarko Zec } 3162a3f93b72SMarko Zec } 3163a3f93b72SMarko Zec if (node != NULL) 3164a3f93b72SMarko Zec break; 3165a3f93b72SMarko Zec } 3166687adb70SGleb Smirnoff IDHASH_RUNLOCK(); 3167a3f93b72SMarko Zec 3168a3f93b72SMarko Zec /* Attempt to kill it only if it is a regular node */ 3169a3f93b72SMarko Zec if (node != NULL) { 3170bc29160dSMarko Zec if (node == last_killed) { 3171bc29160dSMarko Zec /* This should never happen */ 31724bd1b557SGleb Smirnoff printf("ng node %s needs NGF_REALLY_DIE\n", 31734bd1b557SGleb Smirnoff node->nd_name); 3174a3f93b72SMarko Zec if (node->nd_flags & NGF_REALLY_DIE) 3175a3f93b72SMarko Zec panic("ng node %s won't die", 3176a3f93b72SMarko Zec node->nd_name); 3177bc29160dSMarko Zec node->nd_flags |= NGF_REALLY_DIE; 3178bc29160dSMarko Zec } 3179bc29160dSMarko Zec ng_rmnode(node, NULL, NULL, 0); 3180a3f93b72SMarko Zec NG_NODE_UNREF(node); 3181bc29160dSMarko Zec last_killed = node; 3182bc29160dSMarko Zec } 3183a3f93b72SMarko Zec } while (node != NULL); 3184687adb70SGleb Smirnoff 3185687adb70SGleb Smirnoff hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask); 3186687adb70SGleb Smirnoff hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_ID_hmask); 3187bc29160dSMarko Zec } 3188320d00eeSGleb Smirnoff VNET_SYSUNINIT(vnet_netgraph_uninit, SI_SUB_NETGRAPH, SI_ORDER_FIRST, 3189d0728d71SRobert Watson vnet_netgraph_uninit, NULL); 3190bc29160dSMarko Zec #endif /* VIMAGE */ 3191bc29160dSMarko Zec 31924cf49a43SJulian Elischer /* 31934cf49a43SJulian Elischer * Handle loading and unloading for this code. 31944cf49a43SJulian Elischer * The only thing we need to link into is the NETISR strucure. 31954cf49a43SJulian Elischer */ 31964cf49a43SJulian Elischer static int 31974cf49a43SJulian Elischer ngb_mod_event(module_t mod, int event, void *data) 31984cf49a43SJulian Elischer { 3199f2fbb838SAlexander Motin struct proc *p; 3200f2fbb838SAlexander Motin struct thread *td; 3201f2fbb838SAlexander Motin int i, error = 0; 32024cf49a43SJulian Elischer 32034cf49a43SJulian Elischer switch (event) { 32044cf49a43SJulian Elischer case MOD_LOAD: 32051489164fSGleb Smirnoff /* Initialize everything. */ 32062c8dda8dSWojciech A. Koszek NG_WORKLIST_LOCK_INIT(); 3207c4282b74SGleb Smirnoff rw_init(&ng_typelist_lock, "netgraph types"); 3208c4282b74SGleb Smirnoff rw_init(&ng_idhash_lock, "netgraph idhash"); 3209c4282b74SGleb Smirnoff rw_init(&ng_namehash_lock, "netgraph namehash"); 3210*d2fd0788SAlexander V. Chernikov rw_init(&ng_topo_lock, "netgraph topology mutex"); 32111489164fSGleb Smirnoff #ifdef NETGRAPH_DEBUG 3212cfea3f85SAlexander Motin mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL, 3213cfea3f85SAlexander Motin MTX_DEF); 32141489164fSGleb Smirnoff mtx_init(&ngq_mtx, "netgraph item list mutex", NULL, 3215efd8e7c9SDon Lewis MTX_DEF); 32161489164fSGleb Smirnoff #endif 32171489164fSGleb Smirnoff ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item), 321877a117caSGleb Smirnoff NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0); 32191489164fSGleb Smirnoff uma_zone_set_max(ng_qzone, maxalloc); 32204bd1b557SGleb Smirnoff ng_qdzone = uma_zcreate("NetGraph data items", 32214bd1b557SGleb Smirnoff sizeof(struct ng_item), NULL, NULL, NULL, NULL, 322277a117caSGleb Smirnoff UMA_ALIGN_CACHE, 0); 32236aa6d011SAlexander Motin uma_zone_set_max(ng_qdzone, maxdata); 3224f2fbb838SAlexander Motin /* Autoconfigure number of threads. */ 3225f2fbb838SAlexander Motin if (numthreads <= 0) 3226f2fbb838SAlexander Motin numthreads = mp_ncpus; 3227f2fbb838SAlexander Motin /* Create threads. */ 3228f2fbb838SAlexander Motin p = NULL; /* start with no process */ 3229f2fbb838SAlexander Motin for (i = 0; i < numthreads; i++) { 3230f2fbb838SAlexander Motin if (kproc_kthread_add(ngthread, NULL, &p, &td, 3231f2fbb838SAlexander Motin RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) { 3232f2fbb838SAlexander Motin numthreads = i; 3233f2fbb838SAlexander Motin break; 3234f2fbb838SAlexander Motin } 3235f2fbb838SAlexander Motin } 32364cf49a43SJulian Elischer break; 32374cf49a43SJulian Elischer case MOD_UNLOAD: 323864efc707SRobert Watson /* You can't unload it because an interface may be using it. */ 32394cf49a43SJulian Elischer error = EBUSY; 32404cf49a43SJulian Elischer break; 32414cf49a43SJulian Elischer default: 32424cf49a43SJulian Elischer error = EOPNOTSUPP; 32434cf49a43SJulian Elischer break; 32444cf49a43SJulian Elischer } 32454cf49a43SJulian Elischer return (error); 32464cf49a43SJulian Elischer } 32474cf49a43SJulian Elischer 32484cf49a43SJulian Elischer static moduledata_t netgraph_mod = { 32494cf49a43SJulian Elischer "netgraph", 32504cf49a43SJulian Elischer ngb_mod_event, 32514cf49a43SJulian Elischer (NULL) 32524cf49a43SJulian Elischer }; 3253320d00eeSGleb Smirnoff DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_FIRST); 3254bfa7e882SJulian Elischer SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family"); 3255bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, 0, NG_ABI_VERSION,""); 3256bfa7e882SJulian Elischer SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, 0, NG_VERSION, ""); 32574cf49a43SJulian Elischer 325830400f03SJulian Elischer #ifdef NETGRAPH_DEBUG 325930400f03SJulian Elischer void 326030400f03SJulian Elischer dumphook (hook_p hook, char *file, int line) 326130400f03SJulian Elischer { 326230400f03SJulian Elischer printf("hook: name %s, %d refs, Last touched:\n", 326330400f03SJulian Elischer _NG_HOOK_NAME(hook), hook->hk_refs); 326430400f03SJulian Elischer printf(" Last active @ %s, line %d\n", 326530400f03SJulian Elischer hook->lastfile, hook->lastline); 326630400f03SJulian Elischer if (line) { 326730400f03SJulian Elischer printf(" problem discovered at file %s, line %d\n", file, line); 3268e5fe87b3SGleb Smirnoff #ifdef KDB 3269e5fe87b3SGleb Smirnoff kdb_backtrace(); 3270e5fe87b3SGleb Smirnoff #endif 327130400f03SJulian Elischer } 327230400f03SJulian Elischer } 327330400f03SJulian Elischer 327430400f03SJulian Elischer void 327530400f03SJulian Elischer dumpnode(node_p node, char *file, int line) 327630400f03SJulian Elischer { 327730400f03SJulian Elischer printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n", 32786b795970SJulian Elischer _NG_NODE_ID(node), node->nd_type->name, 327930400f03SJulian Elischer node->nd_numhooks, node->nd_flags, 328030400f03SJulian Elischer node->nd_refs, node->nd_name); 328130400f03SJulian Elischer printf(" Last active @ %s, line %d\n", 328230400f03SJulian Elischer node->lastfile, node->lastline); 328330400f03SJulian Elischer if (line) { 328430400f03SJulian Elischer printf(" problem discovered at file %s, line %d\n", file, line); 3285e5fe87b3SGleb Smirnoff #ifdef KDB 3286e5fe87b3SGleb Smirnoff kdb_backtrace(); 3287e5fe87b3SGleb Smirnoff #endif 328830400f03SJulian Elischer } 328930400f03SJulian Elischer } 329030400f03SJulian Elischer 3291069154d5SJulian Elischer void 3292069154d5SJulian Elischer dumpitem(item_p item, char *file, int line) 3293069154d5SJulian Elischer { 3294069154d5SJulian Elischer printf(" ACTIVE item, last used at %s, line %d", 3295069154d5SJulian Elischer item->lastfile, item->lastline); 32966b795970SJulian Elischer switch(item->el_flags & NGQF_TYPE) { 32976b795970SJulian Elischer case NGQF_DATA: 3298069154d5SJulian Elischer printf(" - [data]\n"); 32996b795970SJulian Elischer break; 33006b795970SJulian Elischer case NGQF_MESG: 33016b795970SJulian Elischer printf(" - retaddr[%d]:\n", _NGI_RETADDR(item)); 33026b795970SJulian Elischer break; 33036b795970SJulian Elischer case NGQF_FN: 3304857304e6SRuslan Ermilov printf(" - fn@%p (%p, %p, %p, %d (%x))\n", 3305857304e6SRuslan Ermilov _NGI_FN(item), 3306857304e6SRuslan Ermilov _NGI_NODE(item), 3307857304e6SRuslan Ermilov _NGI_HOOK(item), 3308857304e6SRuslan Ermilov item->body.fn.fn_arg1, 3309857304e6SRuslan Ermilov item->body.fn.fn_arg2, 3310857304e6SRuslan Ermilov item->body.fn.fn_arg2); 3311857304e6SRuslan Ermilov break; 3312e088dd4cSAlexander Motin case NGQF_FN2: 3313eb4687d2SAlexander Motin printf(" - fn2@%p (%p, %p, %p, %d (%x))\n", 3314857304e6SRuslan Ermilov _NGI_FN2(item), 33156064e568SGleb Smirnoff _NGI_NODE(item), 33166064e568SGleb Smirnoff _NGI_HOOK(item), 33176b795970SJulian Elischer item->body.fn.fn_arg1, 33186b795970SJulian Elischer item->body.fn.fn_arg2, 33196b795970SJulian Elischer item->body.fn.fn_arg2); 33206b795970SJulian Elischer break; 3321069154d5SJulian Elischer } 332230400f03SJulian Elischer if (line) { 3323069154d5SJulian Elischer printf(" problem discovered at file %s, line %d\n", file, line); 33246064e568SGleb Smirnoff if (_NGI_NODE(item)) { 332530400f03SJulian Elischer printf("node %p ([%x])\n", 33266064e568SGleb Smirnoff _NGI_NODE(item), ng_node2ID(_NGI_NODE(item))); 3327069154d5SJulian Elischer } 332830400f03SJulian Elischer } 332930400f03SJulian Elischer } 333030400f03SJulian Elischer 333130400f03SJulian Elischer static void 333230400f03SJulian Elischer ng_dumpitems(void) 333330400f03SJulian Elischer { 333430400f03SJulian Elischer item_p item; 333530400f03SJulian Elischer int i = 1; 333630400f03SJulian Elischer TAILQ_FOREACH(item, &ng_itemlist, all) { 333730400f03SJulian Elischer printf("[%d] ", i++); 333830400f03SJulian Elischer dumpitem(item, NULL, 0); 333930400f03SJulian Elischer } 334030400f03SJulian Elischer } 334130400f03SJulian Elischer 334230400f03SJulian Elischer static void 334330400f03SJulian Elischer ng_dumpnodes(void) 334430400f03SJulian Elischer { 334530400f03SJulian Elischer node_p node; 334630400f03SJulian Elischer int i = 1; 334753f9c5e9SRobert Watson mtx_lock(&ng_nodelist_mtx); 334830400f03SJulian Elischer SLIST_FOREACH(node, &ng_allnodes, nd_all) { 334930400f03SJulian Elischer printf("[%d] ", i++); 335030400f03SJulian Elischer dumpnode(node, NULL, 0); 335130400f03SJulian Elischer } 335253f9c5e9SRobert Watson mtx_unlock(&ng_nodelist_mtx); 335330400f03SJulian Elischer } 335430400f03SJulian Elischer 335530400f03SJulian Elischer static void 335630400f03SJulian Elischer ng_dumphooks(void) 335730400f03SJulian Elischer { 335830400f03SJulian Elischer hook_p hook; 335930400f03SJulian Elischer int i = 1; 336053f9c5e9SRobert Watson mtx_lock(&ng_nodelist_mtx); 336130400f03SJulian Elischer SLIST_FOREACH(hook, &ng_allhooks, hk_all) { 336230400f03SJulian Elischer printf("[%d] ", i++); 336330400f03SJulian Elischer dumphook(hook, NULL, 0); 336430400f03SJulian Elischer } 336553f9c5e9SRobert Watson mtx_unlock(&ng_nodelist_mtx); 336630400f03SJulian Elischer } 3367069154d5SJulian Elischer 3368069154d5SJulian Elischer static int 3369069154d5SJulian Elischer sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS) 3370069154d5SJulian Elischer { 3371069154d5SJulian Elischer int error; 3372069154d5SJulian Elischer int val; 3373069154d5SJulian Elischer int i; 3374069154d5SJulian Elischer 3375069154d5SJulian Elischer val = allocated; 3376069154d5SJulian Elischer i = 1; 3377041b706bSDavid Malone error = sysctl_handle_int(oidp, &val, 0, req); 33786b795970SJulian Elischer if (error != 0 || req->newptr == NULL) 33796b795970SJulian Elischer return (error); 33806b795970SJulian Elischer if (val == 42) { 338130400f03SJulian Elischer ng_dumpitems(); 338230400f03SJulian Elischer ng_dumpnodes(); 338330400f03SJulian Elischer ng_dumphooks(); 3384069154d5SJulian Elischer } 33856b795970SJulian Elischer return (0); 3386069154d5SJulian Elischer } 3387069154d5SJulian Elischer 33886b795970SJulian Elischer SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW, 33896b795970SJulian Elischer 0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items"); 339030400f03SJulian Elischer #endif /* NETGRAPH_DEBUG */ 3391069154d5SJulian Elischer 3392069154d5SJulian Elischer /*********************************************************************** 3393069154d5SJulian Elischer * Worklist routines 3394069154d5SJulian Elischer **********************************************************************/ 3395069154d5SJulian Elischer /* 3396069154d5SJulian Elischer * Pick a node off the list of nodes with work, 3397f2fbb838SAlexander Motin * try get an item to process off it. Remove the node from the list. 3398069154d5SJulian Elischer */ 3399069154d5SJulian Elischer static void 3400f2fbb838SAlexander Motin ngthread(void *arg) 3401069154d5SJulian Elischer { 3402069154d5SJulian Elischer for (;;) { 3403394cb30aSAlexander Motin node_p node; 3404394cb30aSAlexander Motin 3405394cb30aSAlexander Motin /* Get node from the worklist. */ 34062c8dda8dSWojciech A. Koszek NG_WORKLIST_LOCK(); 3407f2fbb838SAlexander Motin while ((node = STAILQ_FIRST(&ng_worklist)) == NULL) 3408f2fbb838SAlexander Motin NG_WORKLIST_SLEEP(); 34099852972bSAlexander Motin STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work); 34102c8dda8dSWojciech A. Koszek NG_WORKLIST_UNLOCK(); 3411bc29160dSMarko Zec CURVNET_SET(node->nd_vnet); 34122955ee18SGleb Smirnoff CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist", 34132955ee18SGleb Smirnoff __func__, node->nd_ID, node); 3414069154d5SJulian Elischer /* 3415069154d5SJulian Elischer * We have the node. We also take over the reference 3416069154d5SJulian Elischer * that the list had on it. 3417069154d5SJulian Elischer * Now process as much as you can, until it won't 3418069154d5SJulian Elischer * let you have another item off the queue. 3419069154d5SJulian Elischer * All this time, keep the reference 3420069154d5SJulian Elischer * that lets us be sure that the node still exists. 3421069154d5SJulian Elischer * Let the reference go at the last minute. 3422069154d5SJulian Elischer */ 3423069154d5SJulian Elischer for (;;) { 3424394cb30aSAlexander Motin item_p item; 3425714fb865SGleb Smirnoff int rw; 3426714fb865SGleb Smirnoff 34272c8dda8dSWojciech A. Koszek NG_QUEUE_LOCK(&node->nd_input_queue); 34289852972bSAlexander Motin item = ng_dequeue(node, &rw); 3429069154d5SJulian Elischer if (item == NULL) { 34309852972bSAlexander Motin node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ; 34312c8dda8dSWojciech A. Koszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 3432069154d5SJulian Elischer break; /* go look for another node */ 3433069154d5SJulian Elischer } else { 34342c8dda8dSWojciech A. Koszek NG_QUEUE_UNLOCK(&node->nd_input_queue); 34355951069aSJulian Elischer NGI_GET_NODE(item, node); /* zaps stored node */ 3436714fb865SGleb Smirnoff ng_apply_item(node, item, rw); 34375951069aSJulian Elischer NG_NODE_UNREF(node); 3438069154d5SJulian Elischer } 3439069154d5SJulian Elischer } 3440a96dcd84SJulian Elischer NG_NODE_UNREF(node); 3441bc29160dSMarko Zec CURVNET_RESTORE(); 3442069154d5SJulian Elischer } 3443069154d5SJulian Elischer } 3444069154d5SJulian Elischer 344533338e73SJulian Elischer /* 344633338e73SJulian Elischer * XXX 344733338e73SJulian Elischer * It's posible that a debugging NG_NODE_REF may need 344833338e73SJulian Elischer * to be outside the mutex zone 344933338e73SJulian Elischer */ 3450069154d5SJulian Elischer static void 3451394cb30aSAlexander Motin ng_worklist_add(node_p node) 3452069154d5SJulian Elischer { 3453f912c0f7SGleb Smirnoff 34545bc15201SGleb Smirnoff mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED); 3455f912c0f7SGleb Smirnoff 34569852972bSAlexander Motin if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) { 3457069154d5SJulian Elischer /* 3458069154d5SJulian Elischer * If we are not already on the work queue, 3459069154d5SJulian Elischer * then put us on. 3460069154d5SJulian Elischer */ 34619852972bSAlexander Motin node->nd_input_queue.q_flags2 |= NGQ2_WORKQ; 34624bd1b557SGleb Smirnoff NG_NODE_REF(node); /* XXX safe in mutex? */ 34632c8dda8dSWojciech A. Koszek NG_WORKLIST_LOCK(); 34649852972bSAlexander Motin STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work); 34652c8dda8dSWojciech A. Koszek NG_WORKLIST_UNLOCK(); 34662955ee18SGleb Smirnoff CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__, 34672955ee18SGleb Smirnoff node->nd_ID, node); 3468f2fbb838SAlexander Motin NG_WORKLIST_WAKEUP(); 3469394cb30aSAlexander Motin } else { 34702955ee18SGleb Smirnoff CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist", 34712955ee18SGleb Smirnoff __func__, node->nd_ID, node); 3472394cb30aSAlexander Motin } 3473069154d5SJulian Elischer } 3474069154d5SJulian Elischer 3475069154d5SJulian Elischer /*********************************************************************** 3476069154d5SJulian Elischer * Externally useable functions to set up a queue item ready for sending 3477069154d5SJulian Elischer ***********************************************************************/ 3478069154d5SJulian Elischer 347930400f03SJulian Elischer #ifdef NETGRAPH_DEBUG 348030400f03SJulian Elischer #define ITEM_DEBUG_CHECKS \ 34814cf49a43SJulian Elischer do { \ 34821acb27c6SJulian Elischer if (NGI_NODE(item) ) { \ 3483069154d5SJulian Elischer printf("item already has node"); \ 34843de213ccSRobert Watson kdb_enter(KDB_WHY_NETGRAPH, "has node"); \ 34851acb27c6SJulian Elischer NGI_CLR_NODE(item); \ 3486069154d5SJulian Elischer } \ 34871acb27c6SJulian Elischer if (NGI_HOOK(item) ) { \ 3488069154d5SJulian Elischer printf("item already has hook"); \ 34893de213ccSRobert Watson kdb_enter(KDB_WHY_NETGRAPH, "has hook"); \ 34901acb27c6SJulian Elischer NGI_CLR_HOOK(item); \ 3491069154d5SJulian Elischer } \ 3492069154d5SJulian Elischer } while (0) 3493069154d5SJulian Elischer #else 349430400f03SJulian Elischer #define ITEM_DEBUG_CHECKS 3495069154d5SJulian Elischer #endif 3496069154d5SJulian Elischer 3497069154d5SJulian Elischer /* 34988ed370fdSJulian Elischer * Put mbuf into the item. 3499069154d5SJulian Elischer * Hook and node references will be removed when the item is dequeued. 3500069154d5SJulian Elischer * (or equivalent) 3501069154d5SJulian Elischer * (XXX) Unsafe because no reference held by peer on remote node. 3502069154d5SJulian Elischer * remote node might go away in this timescale. 3503069154d5SJulian Elischer * We know the hooks can't go away because that would require getting 3504069154d5SJulian Elischer * a writer item on both nodes and we must have at least a reader 35054be59335SGleb Smirnoff * here to be able to do this. 3506069154d5SJulian Elischer * Note that the hook loaded is the REMOTE hook. 3507069154d5SJulian Elischer * 3508069154d5SJulian Elischer * This is possibly in the critical path for new data. 3509069154d5SJulian Elischer */ 3510069154d5SJulian Elischer item_p 351142282202SGleb Smirnoff ng_package_data(struct mbuf *m, int flags) 3512069154d5SJulian Elischer { 3513069154d5SJulian Elischer item_p item; 3514069154d5SJulian Elischer 35156aa6d011SAlexander Motin if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) { 3516069154d5SJulian Elischer NG_FREE_M(m); 3517069154d5SJulian Elischer return (NULL); 3518069154d5SJulian Elischer } 351930400f03SJulian Elischer ITEM_DEBUG_CHECKS; 35206aa6d011SAlexander Motin item->el_flags |= NGQF_READER; 3521069154d5SJulian Elischer NGI_M(item) = m; 3522069154d5SJulian Elischer return (item); 3523069154d5SJulian Elischer } 3524069154d5SJulian Elischer 3525069154d5SJulian Elischer /* 3526069154d5SJulian Elischer * Allocate a queue item and put items into it.. 3527069154d5SJulian Elischer * Evaluate the address as this will be needed to queue it and 3528069154d5SJulian Elischer * to work out what some of the fields should be. 3529069154d5SJulian Elischer * Hook and node references will be removed when the item is dequeued. 3530069154d5SJulian Elischer * (or equivalent) 3531069154d5SJulian Elischer */ 3532069154d5SJulian Elischer item_p 353342282202SGleb Smirnoff ng_package_msg(struct ng_mesg *msg, int flags) 3534069154d5SJulian Elischer { 3535069154d5SJulian Elischer item_p item; 3536069154d5SJulian Elischer 35376aa6d011SAlexander Motin if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) { 3538069154d5SJulian Elischer NG_FREE_MSG(msg); 3539069154d5SJulian Elischer return (NULL); 3540069154d5SJulian Elischer } 354130400f03SJulian Elischer ITEM_DEBUG_CHECKS; 35426f683eeeSGleb Smirnoff /* Messages items count as writers unless explicitly exempted. */ 35436f683eeeSGleb Smirnoff if (msg->header.cmd & NGM_READONLY) 35446aa6d011SAlexander Motin item->el_flags |= NGQF_READER; 35456f683eeeSGleb Smirnoff else 35466aa6d011SAlexander Motin item->el_flags |= NGQF_WRITER; 3547069154d5SJulian Elischer /* 3548069154d5SJulian Elischer * Set the current lasthook into the queue item 3549069154d5SJulian Elischer */ 3550069154d5SJulian Elischer NGI_MSG(item) = msg; 3551facfd889SArchie Cobbs NGI_RETADDR(item) = 0; 3552069154d5SJulian Elischer return (item); 3553069154d5SJulian Elischer } 3554069154d5SJulian Elischer 35551acb27c6SJulian Elischer #define SET_RETADDR(item, here, retaddr) \ 35566b795970SJulian Elischer do { /* Data or fn items don't have retaddrs */ \ 35576b795970SJulian Elischer if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) { \ 3558069154d5SJulian Elischer if (retaddr) { \ 3559069154d5SJulian Elischer NGI_RETADDR(item) = retaddr; \ 35604cf49a43SJulian Elischer } else { \ 3561069154d5SJulian Elischer /* \ 3562069154d5SJulian Elischer * The old return address should be ok. \ 3563069154d5SJulian Elischer * If there isn't one, use the address \ 3564069154d5SJulian Elischer * here. \ 3565069154d5SJulian Elischer */ \ 3566069154d5SJulian Elischer if (NGI_RETADDR(item) == 0) { \ 3567069154d5SJulian Elischer NGI_RETADDR(item) \ 3568069154d5SJulian Elischer = ng_node2ID(here); \ 3569069154d5SJulian Elischer } \ 3570069154d5SJulian Elischer } \ 35714cf49a43SJulian Elischer } \ 35724cf49a43SJulian Elischer } while (0) 35734cf49a43SJulian Elischer 35744cf49a43SJulian Elischer int 3575069154d5SJulian Elischer ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr) 35764cf49a43SJulian Elischer { 35771acb27c6SJulian Elischer hook_p peer; 35781acb27c6SJulian Elischer node_p peernode; 357930400f03SJulian Elischer ITEM_DEBUG_CHECKS; 3580069154d5SJulian Elischer /* 3581069154d5SJulian Elischer * Quick sanity check.. 358230400f03SJulian Elischer * Since a hook holds a reference on it's node, once we know 358330400f03SJulian Elischer * that the peer is still connected (even if invalid,) we know 358430400f03SJulian Elischer * that the peer node is present, though maybe invalid. 3585069154d5SJulian Elischer */ 3586*d2fd0788SAlexander V. Chernikov TOPOLOGY_RLOCK(); 35874bd1b557SGleb Smirnoff if ((hook == NULL) || NG_HOOK_NOT_VALID(hook) || 3588a04e9846SAlexander Motin NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) || 3589a04e9846SAlexander Motin NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) { 3590069154d5SJulian Elischer NG_FREE_ITEM(item); 35916b795970SJulian Elischer TRAP_ERROR(); 3592*d2fd0788SAlexander V. Chernikov TOPOLOGY_RUNLOCK(); 3593e08d3e3cSJulian Elischer return (ENETDOWN); 35944cf49a43SJulian Elischer } 35954cf49a43SJulian Elischer 35964cf49a43SJulian Elischer /* 3597069154d5SJulian Elischer * Transfer our interest to the other (peer) end. 35984cf49a43SJulian Elischer */ 35991acb27c6SJulian Elischer NG_HOOK_REF(peer); 36001acb27c6SJulian Elischer NG_NODE_REF(peernode); 3601a04e9846SAlexander Motin NGI_SET_HOOK(item, peer); 36021acb27c6SJulian Elischer NGI_SET_NODE(item, peernode); 36038b68f82fSJulian Elischer SET_RETADDR(item, here, retaddr); 3604a7da736aSGleb Smirnoff 3605*d2fd0788SAlexander V. Chernikov TOPOLOGY_RUNLOCK(); 3606a7da736aSGleb Smirnoff 3607069154d5SJulian Elischer return (0); 3608069154d5SJulian Elischer } 3609069154d5SJulian Elischer 36104cf49a43SJulian Elischer int 3611707d2058SMax Khon ng_address_path(node_p here, item_p item, const char *address, ng_ID_t retaddr) 36124cf49a43SJulian Elischer { 36134cf49a43SJulian Elischer node_p dest = NULL; 3614069154d5SJulian Elischer hook_p hook = NULL; 36154cf49a43SJulian Elischer int error; 3616069154d5SJulian Elischer 361730400f03SJulian Elischer ITEM_DEBUG_CHECKS; 3618069154d5SJulian Elischer /* 3619069154d5SJulian Elischer * Note that ng_path2noderef increments the reference count 3620069154d5SJulian Elischer * on the node for us if it finds one. So we don't have to. 3621069154d5SJulian Elischer */ 3622069154d5SJulian Elischer error = ng_path2noderef(here, address, &dest, &hook); 3623069154d5SJulian Elischer if (error) { 3624069154d5SJulian Elischer NG_FREE_ITEM(item); 362530400f03SJulian Elischer return (error); 3626069154d5SJulian Elischer } 36271acb27c6SJulian Elischer NGI_SET_NODE(item, dest); 3628a7da736aSGleb Smirnoff if (hook) 36291acb27c6SJulian Elischer NGI_SET_HOOK(item, hook); 3630a7da736aSGleb Smirnoff 36311acb27c6SJulian Elischer SET_RETADDR(item, here, retaddr); 3632069154d5SJulian Elischer return (0); 3633069154d5SJulian Elischer } 3634069154d5SJulian Elischer 3635069154d5SJulian Elischer int 3636069154d5SJulian Elischer ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr) 3637069154d5SJulian Elischer { 3638069154d5SJulian Elischer node_p dest; 3639069154d5SJulian Elischer 364030400f03SJulian Elischer ITEM_DEBUG_CHECKS; 3641069154d5SJulian Elischer /* 3642069154d5SJulian Elischer * Find the target node. 3643069154d5SJulian Elischer */ 3644069154d5SJulian Elischer dest = ng_ID2noderef(ID); /* GETS REFERENCE! */ 3645069154d5SJulian Elischer if (dest == NULL) { 3646069154d5SJulian Elischer NG_FREE_ITEM(item); 36476b795970SJulian Elischer TRAP_ERROR(); 3648069154d5SJulian Elischer return(EINVAL); 3649069154d5SJulian Elischer } 3650069154d5SJulian Elischer /* Fill out the contents */ 36511acb27c6SJulian Elischer NGI_SET_NODE(item, dest); 36521acb27c6SJulian Elischer NGI_CLR_HOOK(item); 36531acb27c6SJulian Elischer SET_RETADDR(item, here, retaddr); 3654069154d5SJulian Elischer return (0); 3655069154d5SJulian Elischer } 3656069154d5SJulian Elischer 3657069154d5SJulian Elischer /* 3658069154d5SJulian Elischer * special case to send a message to self (e.g. destroy node) 3659069154d5SJulian Elischer * Possibly indicate an arrival hook too. 3660069154d5SJulian Elischer * Useful for removing that hook :-) 3661069154d5SJulian Elischer */ 3662069154d5SJulian Elischer item_p 3663069154d5SJulian Elischer ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg) 3664069154d5SJulian Elischer { 3665069154d5SJulian Elischer item_p item; 36664cf49a43SJulian Elischer 3667859a4d16SJulian Elischer /* 3668859a4d16SJulian Elischer * Find the target node. 3669859a4d16SJulian Elischer * If there is a HOOK argument, then use that in preference 3670859a4d16SJulian Elischer * to the address. 3671859a4d16SJulian Elischer */ 36726aa6d011SAlexander Motin if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) { 3673069154d5SJulian Elischer NG_FREE_MSG(msg); 3674069154d5SJulian Elischer return (NULL); 36754cf49a43SJulian Elischer } 36764cf49a43SJulian Elischer 36774cf49a43SJulian Elischer /* Fill out the contents */ 36786aa6d011SAlexander Motin item->el_flags |= NGQF_WRITER; 367930400f03SJulian Elischer NG_NODE_REF(here); 36801acb27c6SJulian Elischer NGI_SET_NODE(item, here); 36811acb27c6SJulian Elischer if (hook) { 368230400f03SJulian Elischer NG_HOOK_REF(hook); 36831acb27c6SJulian Elischer NGI_SET_HOOK(item, hook); 36841acb27c6SJulian Elischer } 3685069154d5SJulian Elischer NGI_MSG(item) = msg; 3686069154d5SJulian Elischer NGI_RETADDR(item) = ng_node2ID(here); 3687069154d5SJulian Elischer return (item); 36884cf49a43SJulian Elischer } 36894cf49a43SJulian Elischer 3690e088dd4cSAlexander Motin /* 3691e088dd4cSAlexander Motin * Send ng_item_fn function call to the specified node. 3692e088dd4cSAlexander Motin */ 3693e088dd4cSAlexander Motin 369442282202SGleb Smirnoff int 3695b332b91fSGleb Smirnoff ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2) 3696b332b91fSGleb Smirnoff { 3697b332b91fSGleb Smirnoff 3698b332b91fSGleb Smirnoff return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS); 3699b332b91fSGleb Smirnoff } 3700b332b91fSGleb Smirnoff 3701b332b91fSGleb Smirnoff int 3702aacdb114SGleb Smirnoff ng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2, 370342282202SGleb Smirnoff int flags) 37046b795970SJulian Elischer { 37056b795970SJulian Elischer item_p item; 37066b795970SJulian Elischer 37076aa6d011SAlexander Motin if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) { 37086b795970SJulian Elischer return (ENOMEM); 37096b795970SJulian Elischer } 37106aa6d011SAlexander Motin item->el_flags |= NGQF_WRITER; 3711a96dcd84SJulian Elischer NG_NODE_REF(node); /* and one for the item */ 37121acb27c6SJulian Elischer NGI_SET_NODE(item, node); 37131acb27c6SJulian Elischer if (hook) { 37146b795970SJulian Elischer NG_HOOK_REF(hook); 37151acb27c6SJulian Elischer NGI_SET_HOOK(item, hook); 37166b795970SJulian Elischer } 37176b795970SJulian Elischer NGI_FN(item) = fn; 37186b795970SJulian Elischer NGI_ARG1(item) = arg1; 37196b795970SJulian Elischer NGI_ARG2(item) = arg2; 372042282202SGleb Smirnoff return(ng_snd_item(item, flags)); 37216b795970SJulian Elischer } 37226b795970SJulian Elischer 37234cf49a43SJulian Elischer /* 3724b332b91fSGleb Smirnoff * Send ng_item_fn2 function call to the specified node. 3725b332b91fSGleb Smirnoff * 3726b332b91fSGleb Smirnoff * If an optional pitem parameter is supplied, its apply 3727b332b91fSGleb Smirnoff * callback will be copied to the new item. If also NG_REUSE_ITEM 3728b332b91fSGleb Smirnoff * flag is set, no new item will be allocated, but pitem will 3729b332b91fSGleb Smirnoff * be used. 3730e088dd4cSAlexander Motin */ 3731e088dd4cSAlexander Motin int 3732b332b91fSGleb Smirnoff ng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1, 3733b332b91fSGleb Smirnoff int arg2, int flags) 3734e088dd4cSAlexander Motin { 3735e088dd4cSAlexander Motin item_p item; 3736e088dd4cSAlexander Motin 3737b332b91fSGleb Smirnoff KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0), 3738b332b91fSGleb Smirnoff ("%s: NG_REUSE_ITEM but no pitem", __func__)); 3739e088dd4cSAlexander Motin 3740e088dd4cSAlexander Motin /* 3741b332b91fSGleb Smirnoff * Allocate a new item if no supplied or 3742b332b91fSGleb Smirnoff * if we can't use supplied one. 3743e088dd4cSAlexander Motin */ 3744b332b91fSGleb Smirnoff if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) { 37456aa6d011SAlexander Motin if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL) 3746e088dd4cSAlexander Motin return (ENOMEM); 37476aa6d011SAlexander Motin if (pitem != NULL) 37486aa6d011SAlexander Motin item->apply = pitem->apply; 3749ed75521fSAlexander Motin } else { 37506aa6d011SAlexander Motin if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL) 37516aa6d011SAlexander Motin return (ENOMEM); 3752ed75521fSAlexander Motin } 3753b332b91fSGleb Smirnoff 37546aa6d011SAlexander Motin item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER; 3755e088dd4cSAlexander Motin NG_NODE_REF(node); /* and one for the item */ 3756e088dd4cSAlexander Motin NGI_SET_NODE(item, node); 3757e088dd4cSAlexander Motin if (hook) { 3758e088dd4cSAlexander Motin NG_HOOK_REF(hook); 3759e088dd4cSAlexander Motin NGI_SET_HOOK(item, hook); 3760e088dd4cSAlexander Motin } 3761e088dd4cSAlexander Motin NGI_FN2(item) = fn; 3762e088dd4cSAlexander Motin NGI_ARG1(item) = arg1; 3763e088dd4cSAlexander Motin NGI_ARG2(item) = arg2; 3764e088dd4cSAlexander Motin return(ng_snd_item(item, flags)); 3765e088dd4cSAlexander Motin } 3766e088dd4cSAlexander Motin 3767e088dd4cSAlexander Motin /* 3768d2ca21a9SJulian Elischer * Official timeout routines for Netgraph nodes. 3769d2ca21a9SJulian Elischer */ 3770d2ca21a9SJulian Elischer static void 37711fbb36ffSGleb Smirnoff ng_callout_trampoline(void *arg) 3772d2ca21a9SJulian Elischer { 3773d2ca21a9SJulian Elischer item_p item = arg; 3774d2ca21a9SJulian Elischer 3775bc29160dSMarko Zec CURVNET_SET(NGI_NODE(item)->nd_vnet); 3776d2ca21a9SJulian Elischer ng_snd_item(item, 0); 3777bc29160dSMarko Zec CURVNET_RESTORE(); 3778d2ca21a9SJulian Elischer } 3779d2ca21a9SJulian Elischer 378030bef41bSGleb Smirnoff int 3781f9d9e1b4SGleb Smirnoff ng_callout(struct callout *c, node_p node, hook_p hook, int ticks, 3782d2ca21a9SJulian Elischer ng_item_fn *fn, void * arg1, int arg2) 3783d2ca21a9SJulian Elischer { 37841bf8e0faSGleb Smirnoff item_p item, oitem; 3785d2ca21a9SJulian Elischer 37866aa6d011SAlexander Motin if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL) 378730bef41bSGleb Smirnoff return (ENOMEM); 378830bef41bSGleb Smirnoff 37896aa6d011SAlexander Motin item->el_flags |= NGQF_WRITER; 3790d2ca21a9SJulian Elischer NG_NODE_REF(node); /* and one for the item */ 3791d2ca21a9SJulian Elischer NGI_SET_NODE(item, node); 3792d2ca21a9SJulian Elischer if (hook) { 3793d2ca21a9SJulian Elischer NG_HOOK_REF(hook); 3794d2ca21a9SJulian Elischer NGI_SET_HOOK(item, hook); 3795d2ca21a9SJulian Elischer } 3796d2ca21a9SJulian Elischer NGI_FN(item) = fn; 3797d2ca21a9SJulian Elischer NGI_ARG1(item) = arg1; 3798d2ca21a9SJulian Elischer NGI_ARG2(item) = arg2; 37991bf8e0faSGleb Smirnoff oitem = c->c_arg; 38001bf8e0faSGleb Smirnoff if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 && 38011bf8e0faSGleb Smirnoff oitem != NULL) 38021bf8e0faSGleb Smirnoff NG_FREE_ITEM(oitem); 380330bef41bSGleb Smirnoff return (0); 3804d2ca21a9SJulian Elischer } 3805d2ca21a9SJulian Elischer 3806d2ca21a9SJulian Elischer /* A special modified version of untimeout() */ 3807d2ca21a9SJulian Elischer int 3808f9d9e1b4SGleb Smirnoff ng_uncallout(struct callout *c, node_p node) 3809d2ca21a9SJulian Elischer { 3810d2ca21a9SJulian Elischer item_p item; 381130bef41bSGleb Smirnoff int rval; 3812d2ca21a9SJulian Elischer 381303b25f5dSGleb Smirnoff KASSERT(c != NULL, ("ng_uncallout: NULL callout")); 381403b25f5dSGleb Smirnoff KASSERT(node != NULL, ("ng_uncallout: NULL node")); 381503b25f5dSGleb Smirnoff 38163eadb26dSGleb Smirnoff rval = callout_stop(c); 381730bef41bSGleb Smirnoff item = c->c_arg; 381830bef41bSGleb Smirnoff /* Do an extra check */ 38191fbb36ffSGleb Smirnoff if ((rval > 0) && (c->c_func == &ng_callout_trampoline) && 382030bef41bSGleb Smirnoff (NGI_NODE(item) == node)) { 3821d2ca21a9SJulian Elischer /* 3822d2ca21a9SJulian Elischer * We successfully removed it from the queue before it ran 3823d2ca21a9SJulian Elischer * So now we need to unreference everything that was 3824d2ca21a9SJulian Elischer * given extra references. (NG_FREE_ITEM does this). 3825d2ca21a9SJulian Elischer */ 3826d2ca21a9SJulian Elischer NG_FREE_ITEM(item); 3827d2ca21a9SJulian Elischer } 38281bf8e0faSGleb Smirnoff c->c_arg = NULL; 382930bef41bSGleb Smirnoff 383030bef41bSGleb Smirnoff return (rval); 3831d2ca21a9SJulian Elischer } 3832d2ca21a9SJulian Elischer 3833d2ca21a9SJulian Elischer /* 3834069154d5SJulian Elischer * Set the address, if none given, give the node here. 38354cf49a43SJulian Elischer */ 3836069154d5SJulian Elischer void 3837069154d5SJulian Elischer ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr) 38384cf49a43SJulian Elischer { 3839069154d5SJulian Elischer if (retaddr) { 3840069154d5SJulian Elischer NGI_RETADDR(item) = retaddr; 3841069154d5SJulian Elischer } else { 3842069154d5SJulian Elischer /* 3843069154d5SJulian Elischer * The old return address should be ok. 3844069154d5SJulian Elischer * If there isn't one, use the address here. 3845069154d5SJulian Elischer */ 3846069154d5SJulian Elischer NGI_RETADDR(item) = ng_node2ID(here); 3847069154d5SJulian Elischer } 3848069154d5SJulian Elischer } 3849