ng_base.c revision 59879
199026Sjulian 299026Sjulian/* 399026Sjulian * ng_base.c 499026Sjulian * 599026Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc. 699026Sjulian * All rights reserved. 799026Sjulian * 899026Sjulian * Subject to the following obligations and disclaimer of warranty, use and 999026Sjulian * redistribution of this software, in source or object code forms, with or 1099026Sjulian * without modifications are expressly permitted by Whistle Communications; 1199026Sjulian * provided, however, that: 1299026Sjulian * 1. Any and all reproductions of the source or object code must include the 1399026Sjulian * copyright notice above and the following disclaimer of warranties; and 1499026Sjulian * 2. No rights are granted, in any manner or form, to use Whistle 1599026Sjulian * Communications, Inc. trademarks, including the mark "WHISTLE 1699026Sjulian * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1799026Sjulian * such appears in the above copyright notice or in the software. 1899026Sjulian * 1999026Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 2099026Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 2199026Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 2299026Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2399026Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 2499026Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2599026Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 2699026Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2799026Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 2899026Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 2999026Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 3099026Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 3199026Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 3299026Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3399026Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3499026Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3599026Sjulian * OF SUCH DAMAGE. 3699026Sjulian * 3799026Sjulian * Authors: Julian Elischer <julian@whistle.com> 3899026Sjulian * Archie Cobbs <archie@whistle.com> 3999026Sjulian * 4099026Sjulian * $FreeBSD: head/sys/netgraph/ng_base.c 59879 2000-05-01 23:29:19Z archie $ 4199026Sjulian * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $ 4299026Sjulian */ 4399026Sjulian 4499026Sjulian/* 4599026Sjulian * This file implements the base netgraph code. 4699026Sjulian */ 4799026Sjulian 4899026Sjulian#include <sys/param.h> 4999026Sjulian#include <sys/systm.h> 5099026Sjulian#include <sys/errno.h> 5199026Sjulian#include <sys/kernel.h> 5299026Sjulian#include <sys/malloc.h> 5399026Sjulian#include <sys/syslog.h> 54100273Speter#include <sys/linker.h> 55100273Speter#include <sys/queue.h> 5699026Sjulian#include <sys/mbuf.h> 5799026Sjulian#include <sys/ctype.h> 5899026Sjulian#include <machine/limits.h> 5999026Sjulian 6099026Sjulian#include <net/netisr.h> 6199026Sjulian 6299026Sjulian#include <netgraph/ng_message.h> 6399026Sjulian#include <netgraph/netgraph.h> 6499026Sjulian#include <netgraph/ng_parse.h> 6599026Sjulian 6699026SjulianMODULE_VERSION(netgraph, 1); 6799026Sjulian 6899026Sjulian/* List of all nodes */ 6999026Sjulianstatic LIST_HEAD(, ng_node) nodelist; 7099026Sjulian 7199026Sjulian/* List of installed types */ 7299026Sjulianstatic LIST_HEAD(, ng_type) typelist; 7399026Sjulian 7499026Sjulian/* Hash releted definitions */ 7599026Sjulian#define ID_HASH_SIZE 32 /* most systems wont need even this many */ 7699026Sjulianstatic LIST_HEAD(, ng_node) ID_hash[ID_HASH_SIZE]; 7799026Sjulian/* Don't nead to initialise them because it's a LIST */ 7899026Sjulian 7999026Sjulian/* Internal functions */ 8099026Sjulianstatic int ng_add_hook(node_p node, const char *name, hook_p * hookp); 8199026Sjulianstatic int ng_connect(hook_p hook1, hook_p hook2); 8299026Sjulianstatic void ng_disconnect_hook(hook_p hook); 8399026Sjulianstatic int ng_generic_msg(node_p here, struct ng_mesg *msg, 8499026Sjulian const char *retaddr, struct ng_mesg ** resp, 8599026Sjulian hook_p hook); 8699026Sjulianstatic ng_ID_t ng_decodeidname(const char *name); 8799026Sjulianstatic int ngb_mod_event(module_t mod, int event, void *data); 8899026Sjulianstatic void ngintr(void); 8999026Sjulian 9099026Sjulian/* Our own netgraph malloc type */ 9199026SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); 9299026Sjulian 9399026Sjulian/* Set this to Debugger("X") to catch all errors as they occur */ 9499026Sjulian#ifndef TRAP_ERROR 9599552Speter#define TRAP_ERROR 9699026Sjulian#endif 9799026Sjulian 9899026Sjulianstatic ng_ID_t nextID = 1; 9999026Sjulian 10099026Sjulian#ifdef INVARIANTS 10199026Sjulian#define CHECK_DATA_MBUF(m) do { \ 10299026Sjulian struct mbuf *n; \ 10399026Sjulian int total; \ 10499026Sjulian \ 10599026Sjulian if (((m)->m_flags & M_PKTHDR) == 0) \ 10699026Sjulian panic("%s: !PKTHDR", __FUNCTION__); \ 10799026Sjulian for (total = 0, n = (m); n != NULL; n = n->m_next) \ 10899026Sjulian total += n->m_len; \ 10999026Sjulian if ((m)->m_pkthdr.len != total) { \ 11099026Sjulian panic("%s: %d != %d", \ 11199026Sjulian __FUNCTION__, (m)->m_pkthdr.len, total); \ 11299026Sjulian } \ 11399552Speter } while (0) 11499026Sjulian#else 11599026Sjulian#define CHECK_DATA_MBUF(m) 11699026Sjulian#endif 11799026Sjulian 11899026Sjulian 11999026Sjulian/************************************************************************ 12099026Sjulian Parse type definitions for generic messages 12199026Sjulian************************************************************************/ 12299026Sjulian 12399026Sjulian/* Handy structure parse type defining macro */ 12499026Sjulian#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ 12599026Sjulianstatic const struct ng_parse_struct_info \ 12699026Sjulian ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args; \ 12799026Sjulianstatic const struct ng_parse_type ng_generic_ ## lo ## _type = { \ 12899026Sjulian &ng_parse_struct_type, \ 12999026Sjulian &ng_ ## lo ## _type_info \ 13099026Sjulian} 13199026Sjulian 13299026SjulianDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); 13399026SjulianDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); 13499026SjulianDEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); 13599026SjulianDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); 13699026SjulianDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); 13799026SjulianDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); 13899026SjulianDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); 13999026Sjulian 14099026Sjulian/* Get length of an array when the length is stored as a 32 bit 14199026Sjulian value immediately preceeding the array -- as with struct namelist 14299026Sjulian and struct typelist. */ 14399026Sjulianstatic int 14499026Sjulianng_generic_list_getLength(const struct ng_parse_type *type, 14599026Sjulian const u_char *start, const u_char *buf) 14699026Sjulian{ 14799026Sjulian return *((const u_int32_t *)(buf - 4)); 14899026Sjulian} 14999026Sjulian 15099026Sjulian/* Get length of the array of struct linkinfo inside a struct hooklist */ 15199026Sjulianstatic int 15299026Sjulianng_generic_linkinfo_getLength(const struct ng_parse_type *type, 15399026Sjulian const u_char *start, const u_char *buf) 15499552Speter{ 15599026Sjulian const struct hooklist *hl = (const struct hooklist *)start; 15699026Sjulian 15799026Sjulian return hl->nodeinfo.hooks; 15899026Sjulian} 15999026Sjulian 16099026Sjulian/* Array type for a variable length array of struct namelist */ 16199026Sjulianstatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = { 16299026Sjulian &ng_generic_nodeinfo_type, 16399026Sjulian &ng_generic_list_getLength 16499026Sjulian}; 16599026Sjulianstatic const struct ng_parse_type ng_generic_nodeinfoarray_type = { 16699026Sjulian &ng_parse_array_type, 16799026Sjulian &ng_nodeinfoarray_type_info 16899026Sjulian}; 16999026Sjulian 17099026Sjulian/* Array type for a variable length array of struct typelist */ 17199026Sjulianstatic const struct ng_parse_array_info ng_typeinfoarray_type_info = { 17299552Speter &ng_generic_typeinfo_type, 17399026Sjulian &ng_generic_list_getLength 17499026Sjulian}; 17599026Sjulianstatic const struct ng_parse_type ng_generic_typeinfoarray_type = { 17699026Sjulian &ng_parse_array_type, 17799026Sjulian &ng_typeinfoarray_type_info 17899026Sjulian}; 17999026Sjulian 18099026Sjulian/* Array type for array of struct linkinfo in struct hooklist */ 18199026Sjulianstatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { 18299026Sjulian &ng_generic_linkinfo_type, 18399026Sjulian &ng_generic_linkinfo_getLength 18499026Sjulian}; 18599026Sjulianstatic const struct ng_parse_type ng_generic_linkinfo_array_type = { 18699026Sjulian &ng_parse_array_type, 18799026Sjulian &ng_generic_linkinfo_array_type_info 18899026Sjulian}; 18999026Sjulian 19099026SjulianDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); 19199026SjulianDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, 19299026Sjulian (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); 193103002SjulianDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, 19499026Sjulian (&ng_generic_nodeinfoarray_type)); 19599026Sjulian 19699026Sjulian/* List of commands and how to convert arguments to/from ASCII */ 19799026Sjulianstatic const struct ng_cmdlist ng_generic_cmds[] = { 19899026Sjulian { 19999026Sjulian NGM_GENERIC_COOKIE, 20099026Sjulian NGM_SHUTDOWN, 20199026Sjulian "shutdown", 20299026Sjulian NULL, 20399026Sjulian NULL 204101177Sjulian }, 20599026Sjulian { 20699026Sjulian NGM_GENERIC_COOKIE, 20799026Sjulian NGM_MKPEER, 20899026Sjulian "mkpeer", 20999026Sjulian &ng_generic_mkpeer_type, 21099026Sjulian NULL 21199026Sjulian }, 21299026Sjulian { 21399026Sjulian NGM_GENERIC_COOKIE, 21499026Sjulian NGM_CONNECT, 21599026Sjulian "connect", 21699026Sjulian &ng_generic_connect_type, 21799026Sjulian NULL 21899026Sjulian }, 21999026Sjulian { 22099026Sjulian NGM_GENERIC_COOKIE, 22199026Sjulian NGM_NAME, 22299026Sjulian "name", 22399026Sjulian &ng_generic_name_type, 22499026Sjulian NULL 22599026Sjulian }, 22699026Sjulian { 22799026Sjulian NGM_GENERIC_COOKIE, 22899026Sjulian NGM_RMHOOK, 22999026Sjulian "rmhook", 23099026Sjulian &ng_generic_rmhook_type, 23199026Sjulian NULL 23299026Sjulian }, 23399026Sjulian { 23499026Sjulian NGM_GENERIC_COOKIE, 23599026Sjulian NGM_NODEINFO, 23699026Sjulian "nodeinfo", 23799026Sjulian NULL, 23899026Sjulian &ng_generic_nodeinfo_type 23999026Sjulian }, 24099026Sjulian { 24199026Sjulian NGM_GENERIC_COOKIE, 24299026Sjulian NGM_LISTHOOKS, 24399026Sjulian "listhooks", 24499026Sjulian NULL, 24599026Sjulian &ng_generic_hooklist_type 24699026Sjulian }, 24799026Sjulian { 24899026Sjulian NGM_GENERIC_COOKIE, 24999026Sjulian NGM_LISTNAMES, 25099026Sjulian "listnames", 25199026Sjulian NULL, 25299026Sjulian &ng_generic_listnodes_type /* same as NGM_LISTNODES */ 25399026Sjulian }, 25499026Sjulian { 25599026Sjulian NGM_GENERIC_COOKIE, 25699026Sjulian NGM_LISTNODES, 25799026Sjulian "listnodes", 25899026Sjulian NULL, 259100271Speter &ng_generic_listnodes_type 260100271Speter }, 261100271Speter { 26299026Sjulian NGM_GENERIC_COOKIE, 26399026Sjulian NGM_LISTTYPES, 26499026Sjulian "listtypes", 26599026Sjulian NULL, 26699026Sjulian &ng_generic_typeinfo_type 26799026Sjulian }, 26899026Sjulian { 26999026Sjulian NGM_GENERIC_COOKIE, 27099026Sjulian NGM_TEXT_STATUS, 27199026Sjulian "textstatus", 27299026Sjulian NULL, 27399026Sjulian &ng_parse_string_type 27499026Sjulian }, 27599026Sjulian { 27699026Sjulian NGM_GENERIC_COOKIE, 27799026Sjulian NGM_ASCII2BINARY, 27899026Sjulian "ascii2binary", 27999026Sjulian &ng_parse_ng_mesg_type, 28099026Sjulian &ng_parse_ng_mesg_type 28199026Sjulian }, 28299026Sjulian { 28399026Sjulian NGM_GENERIC_COOKIE, 28499026Sjulian NGM_BINARY2ASCII, 28599026Sjulian "binary2ascii", 28699026Sjulian &ng_parse_ng_mesg_type, 28799026Sjulian &ng_parse_ng_mesg_type 28899026Sjulian }, 28999026Sjulian { 0 } 29099026Sjulian}; 29199026Sjulian 29299026Sjulian/************************************************************************ 29399026Sjulian Node routines 29499026Sjulian************************************************************************/ 29599026Sjulian 29699026Sjulian/* 29799026Sjulian * Instantiate a node of the requested type 29899026Sjulian */ 29999026Sjulianint 30099026Sjulianng_make_node(const char *typename, node_p *nodepp) 30199026Sjulian{ 30299026Sjulian struct ng_type *type; 30399026Sjulian 30499026Sjulian /* Check that the type makes sense */ 30599026Sjulian if (typename == NULL) { 30699026Sjulian TRAP_ERROR; 30799026Sjulian return (EINVAL); 30899026Sjulian } 30999026Sjulian 31099026Sjulian /* Locate the node type */ 311102581Sjulian if ((type = ng_findtype(typename)) == NULL) { 312102581Sjulian char filename[NG_TYPELEN + 4]; 313102581Sjulian linker_file_t lf; 31499026Sjulian int error; 31599026Sjulian 31699026Sjulian /* Not found, try to load it as a loadable module */ 31799026Sjulian snprintf(filename, sizeof(filename), "ng_%s", typename); 31899026Sjulian error = linker_load_file(filename, &lf); 319103002Sjulian if (error != 0) 32099026Sjulian return (error); 32199026Sjulian lf->userrefs++; /* pretend loaded by the syscall */ 32299026Sjulian 32399026Sjulian /* Try again, as now the type should have linked itself in */ 324102581Sjulian if ((type = ng_findtype(typename)) == NULL) 325103002Sjulian return (ENXIO); 326103002Sjulian } 327103002Sjulian 328102581Sjulian /* Call the constructor */ 329103002Sjulian if (type->constructor != NULL) 330103002Sjulian return ((*type->constructor)(nodepp)); 331103002Sjulian else 332103002Sjulian return (ng_make_node_common(type, nodepp)); 333103002Sjulian} 334103002Sjulian 335103002Sjulian/* 336103002Sjulian * Generic node creation. Called by node constructors. 337103002Sjulian * The returned node has a reference count of 1. 338103002Sjulian */ 339103002Sjulianint 340103002Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp) 341103002Sjulian{ 342103002Sjulian node_p node; 343103002Sjulian 344103002Sjulian /* Require the node type to have been already installed */ 345103002Sjulian if (ng_findtype(type->name) == NULL) { 346103002Sjulian TRAP_ERROR; 347103002Sjulian return (EINVAL); 348103002Sjulian } 349103002Sjulian 350103002Sjulian /* Make a node and try attach it to the type */ 351103002Sjulian MALLOC(node, node_p, sizeof(*node), M_NETGRAPH, M_WAITOK); 352103002Sjulian if (node == NULL) { 35399026Sjulian TRAP_ERROR; 354103002Sjulian return (ENOMEM); 355103002Sjulian } 356103002Sjulian bzero(node, sizeof(*node)); 357103002Sjulian node->type = type; 358103002Sjulian node->refs++; /* note reference */ 359103002Sjulian type->refs++; 360103002Sjulian 361103002Sjulian /* Link us into the node linked list */ 36299026Sjulian LIST_INSERT_HEAD(&nodelist, node, nodes); 363103002Sjulian 36499026Sjulian /* Initialize hook list for new node */ 36599026Sjulian LIST_INIT(&node->hooks); 36699026Sjulian 36799026Sjulian /* get an ID and put us in the hash chain */ 36899026Sjulian node->ID = nextID++; /* 137 per second for 1 year before wrap */ 36999026Sjulian LIST_INSERT_HEAD(&ID_hash[node->ID % ID_HASH_SIZE], node, idnodes); 370103002Sjulian 371103002Sjulian /* Done */ 37299026Sjulian *nodepp = node; 37399026Sjulian return (0); 37499026Sjulian} 37599026Sjulian 37699026Sjulian/* 37799026Sjulian * Forceably start the shutdown process on a node. Either call 37899026Sjulian * it's shutdown method, or do the default shutdown if there is 37999026Sjulian * no type-specific method. 38099026Sjulian * 38199026Sjulian * Persistent nodes must have a type-specific method which 38299026Sjulian * resets the NG_INVALID flag. 38399026Sjulian */ 38499026Sjulianvoid 38599026Sjulianng_rmnode(node_p node) 38699026Sjulian{ 387103002Sjulian /* Check if it's already shutting down */ 388103002Sjulian if ((node->flags & NG_INVALID) != 0) 38999026Sjulian return; 39099026Sjulian 39199026Sjulian /* Add an extra reference so it doesn't go away during this */ 39299026Sjulian node->refs++; 39399026Sjulian 39499026Sjulian /* Mark it invalid so any newcomers know not to try use it */ 39599026Sjulian node->flags |= NG_INVALID; 39699026Sjulian 39799026Sjulian /* Ask the type if it has anything to do in this case */ 39899026Sjulian if (node->type && node->type->shutdown) 39999026Sjulian (*node->type->shutdown)(node); 40099026Sjulian else { /* do the default thing */ 40199026Sjulian ng_unname(node); 40299026Sjulian ng_cutlinks(node); 40399026Sjulian ng_unref(node); 40499026Sjulian } 40599026Sjulian 40699026Sjulian /* Remove extra reference, possibly the last */ 40799026Sjulian ng_unref(node); 40899026Sjulian} 40999026Sjulian 41099026Sjulian/* 41199026Sjulian * Called by the destructor to remove any STANDARD external references 41299026Sjulian */ 41399026Sjulianvoid 41499026Sjulianng_cutlinks(node_p node) 41599026Sjulian{ 41699026Sjulian hook_p hook; 41799026Sjulian 41899026Sjulian /* Make sure that this is set to stop infinite loops */ 41999026Sjulian node->flags |= NG_INVALID; 42099026Sjulian 42199026Sjulian /* If we have sleepers, wake them up; they'll see NG_INVALID */ 422103072Sjulian if (node->sleepers) 423103002Sjulian wakeup(node); 424103002Sjulian 425103002Sjulian /* Notify all remaining connected nodes to disconnect */ 42699026Sjulian while ((hook = LIST_FIRST(&node->hooks)) != NULL) 42799026Sjulian ng_destroy_hook(hook); 42899026Sjulian} 42999026Sjulian 43099026Sjulian/* 43199026Sjulian * Remove a reference to the node, possibly the last 43299026Sjulian */ 43399026Sjulianvoid 43499026Sjulianng_unref(node_p node) 43599026Sjulian{ 43699026Sjulian if (--node->refs <= 0) { 43799026Sjulian node->type->refs--; 43899026Sjulian LIST_REMOVE(node, nodes); 43999026Sjulian LIST_REMOVE(node, idnodes); 44099026Sjulian FREE(node, M_NETGRAPH); 44199026Sjulian } 44299026Sjulian} 44399026Sjulian 44499026Sjulian/* 44599026Sjulian * Wait for a node to come ready. Returns a node with a reference count; 44699026Sjulian * don't forget to drop it when we are done with it using ng_release_node(). 44799026Sjulian */ 44899026Sjulianint 44999026Sjulianng_wait_node(node_p node, char *msg) 45099026Sjulian{ 45199026Sjulian int s, error = 0; 45299026Sjulian 45399026Sjulian if (msg == NULL) 45499026Sjulian msg = "netgraph"; 45599026Sjulian s = splnet(); 45699026Sjulian node->sleepers++; 45799026Sjulian node->refs++; /* the sleeping process counts as a reference */ 45899026Sjulian while ((node->flags & (NG_BUSY | NG_INVALID)) == NG_BUSY) 45999026Sjulian error = tsleep(node, (PZERO + 1) | PCATCH, msg, 0); 46099026Sjulian node->sleepers--; 46199026Sjulian if (node->flags & NG_INVALID) { 46299026Sjulian TRAP_ERROR; 46399026Sjulian error = ENXIO; 46499026Sjulian } else { 46599026Sjulian KASSERT(node->refs > 1, 46699026Sjulian ("%s: refs=%d", __FUNCTION__, node->refs)); 46799026Sjulian node->flags |= NG_BUSY; 46899026Sjulian } 46999026Sjulian splx(s); 47099026Sjulian 47199026Sjulian /* Release the reference we had on it */ 47299026Sjulian if (error != 0) 47399026Sjulian ng_unref(node); 47499026Sjulian return error; 47599026Sjulian} 47699026Sjulian 47799026Sjulian/* 47899026Sjulian * Release a node acquired via ng_wait_node() 47999026Sjulian */ 48099026Sjulianvoid 48199026Sjulianng_release_node(node_p node) 48299026Sjulian{ 48399026Sjulian /* Declare that we don't want it */ 48499026Sjulian node->flags &= ~NG_BUSY; 48599026Sjulian 48699026Sjulian /* If we have sleepers, then wake them up */ 48799026Sjulian if (node->sleepers) 48899026Sjulian wakeup(node); 48999026Sjulian 49099026Sjulian /* We also have a reference.. drop it too */ 49199026Sjulian ng_unref(node); 49299026Sjulian} 49399026Sjulian 49499026Sjulian/************************************************************************ 49599026Sjulian Node ID handling 49699026Sjulian************************************************************************/ 49799026Sjulianstatic node_p 49899026Sjulianng_ID2node(ng_ID_t ID) 49999026Sjulian{ 50099026Sjulian node_p np; 50199026Sjulian LIST_FOREACH(np, &ID_hash[ID % ID_HASH_SIZE], idnodes) { 50299026Sjulian if (np->ID == ID) 50399026Sjulian break; 50499026Sjulian } 50599026Sjulian return(np); 50699026Sjulian} 50799026Sjulian 50899026Sjulianng_ID_t 50999026Sjulianng_node2ID(node_p node) 51099026Sjulian{ 51199026Sjulian return (node->ID); 51299026Sjulian} 51399026Sjulian 51499026Sjulian/************************************************************************ 51599026Sjulian Node name handling 51699026Sjulian************************************************************************/ 51799026Sjulian 51899026Sjulian/* 51999026Sjulian * Assign a node a name. Once assigned, the name cannot be changed. 52099026Sjulian */ 52199026Sjulianint 52299026Sjulianng_name_node(node_p node, const char *name) 52399026Sjulian{ 52499026Sjulian int i; 52599026Sjulian 52699026Sjulian /* Check the name is valid */ 52799026Sjulian for (i = 0; i < NG_NODELEN + 1; i++) { 52899026Sjulian if (name[i] == '\0' || name[i] == '.' || name[i] == ':') 52999026Sjulian break; 53099026Sjulian } 53199026Sjulian if (i == 0 || name[i] != '\0') { 53299026Sjulian TRAP_ERROR; 53399026Sjulian return (EINVAL); 53499026Sjulian } 53599026Sjulian if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ 53699026Sjulian TRAP_ERROR; 53799026Sjulian return (EINVAL); 53899026Sjulian } 53999026Sjulian 54099026Sjulian /* Check the node isn't already named */ 54199026Sjulian if (node->name != NULL) { 54299026Sjulian TRAP_ERROR; 54399026Sjulian return (EISCONN); 54499026Sjulian } 54599026Sjulian 54699026Sjulian /* Check the name isn't already being used */ 54799026Sjulian if (ng_findname(node, name) != NULL) { 54899026Sjulian TRAP_ERROR; 54999026Sjulian return (EADDRINUSE); 55099026Sjulian } 55199026Sjulian 55299026Sjulian /* Allocate space and copy it */ 55399026Sjulian MALLOC(node->name, char *, strlen(name) + 1, M_NETGRAPH, M_WAITOK); 55499026Sjulian if (node->name == NULL) { 55599026Sjulian TRAP_ERROR; 55699026Sjulian return (ENOMEM); 55799026Sjulian } 55899026Sjulian strcpy(node->name, name); 55999026Sjulian 56099026Sjulian /* The name counts as a reference */ 56199026Sjulian node->refs++; 56299026Sjulian return (0); 56399026Sjulian} 56499026Sjulian 56599026Sjulian/* 56699026Sjulian * Find a node by absolute name. The name should NOT end with ':' 56799026Sjulian * The name "." means "this node" and "[xxx]" means "the node 56899026Sjulian * with ID (ie, at address) xxx". 56999026Sjulian * 57099026Sjulian * Returns the node if found, else NULL. 57199026Sjulian */ 57299026Sjuliannode_p 57399026Sjulianng_findname(node_p this, const char *name) 57499026Sjulian{ 57599026Sjulian node_p node; 57699026Sjulian ng_ID_t temp; 57799026Sjulian 57899026Sjulian /* "." means "this node" */ 57999026Sjulian if (strcmp(name, ".") == 0) 58099026Sjulian return(this); 58199026Sjulian 58299026Sjulian /* Check for name-by-ID */ 58399026Sjulian if ((temp = ng_decodeidname(name)) != 0) { 58499026Sjulian return (ng_ID2node(temp)); 58599026Sjulian } 58699026Sjulian 58799026Sjulian /* Find node by name */ 58899026Sjulian LIST_FOREACH(node, &nodelist, nodes) { 58999026Sjulian if (node->name != NULL && strcmp(node->name, name) == 0) 59099026Sjulian break; 59199026Sjulian } 59299026Sjulian return (node); 59399026Sjulian} 59499026Sjulian 59599026Sjulian/* 59699026Sjulian * Decode a ID name, eg. "[f03034de]". Returns 0 if the 59799026Sjulian * string is not valid, otherwise returns the value. 59899026Sjulian */ 599100648Sjulianstatic ng_ID_t 600100648Sjulianng_decodeidname(const char *name) 60199026Sjulian{ 60299026Sjulian const int len = strlen(name); 603102950Sdavidxu char *eptr; 60499026Sjulian u_long val; 60599026Sjulian 60699026Sjulian /* Check for proper length, brackets, no leading junk */ 607102950Sdavidxu if (len < 3 || name[0] != '[' || name[len - 1] != ']' 60899026Sjulian || !isxdigit(name[1])) 60999026Sjulian return (0); 61099026Sjulian 61199026Sjulian /* Decode number */ 61299026Sjulian val = strtoul(name + 1, &eptr, 16); 61399026Sjulian if (eptr - name != len - 1 || val == ULONG_MAX || val == 0) 61499026Sjulian return ((ng_ID_t)0); 615102950Sdavidxu return (ng_ID_t)val; 616100648Sjulian} 61799026Sjulian 61899026Sjulian/* 619100653Sjulian * Remove a name from a node. This should only be called 62099026Sjulian * when shutting down and removing the node. 621100648Sjulian */ 62299026Sjulianvoid 62399026Sjulianng_unname(node_p node) 624100648Sjulian{ 62599026Sjulian if (node->name) { 626100648Sjulian FREE(node->name, M_NETGRAPH); 62799026Sjulian node->name = NULL; 62899026Sjulian ng_unref(node); 629100648Sjulian } 630100653Sjulian} 631100653Sjulian 63299026Sjulian/************************************************************************ 63399026Sjulian Hook routines 63499026Sjulian 63599026Sjulian Names are not optional. Hooks are always connected, except for a 636100648Sjulian brief moment within these routines. 63799026Sjulian 63899026Sjulian************************************************************************/ 63999026Sjulian 64099026Sjulian/* 64199026Sjulian * Remove a hook reference 64299026Sjulian */ 64399026Sjulianstatic void 64499026Sjulianng_unref_hook(hook_p hook) 64599026Sjulian{ 64699026Sjulian if (--hook->refs == 0) 64799026Sjulian FREE(hook, M_NETGRAPH); 64899026Sjulian} 64999026Sjulian 65099026Sjulian/* 65199026Sjulian * Add an unconnected hook to a node. Only used internally. 65299026Sjulian */ 65399026Sjulianstatic int 65499026Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp) 65599026Sjulian{ 65699026Sjulian hook_p hook; 65799026Sjulian int error = 0; 65899026Sjulian 65999026Sjulian /* Check that the given name is good */ 66099026Sjulian if (name == NULL) { 66199026Sjulian TRAP_ERROR; 66299026Sjulian return (EINVAL); 66399026Sjulian } 66499026Sjulian if (ng_findhook(node, name) != NULL) { 66599026Sjulian TRAP_ERROR; 66699026Sjulian return (EEXIST); 66799026Sjulian } 66899026Sjulian 66999026Sjulian /* Allocate the hook and link it up */ 67099026Sjulian MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH, M_WAITOK); 67199026Sjulian if (hook == NULL) { 67299026Sjulian TRAP_ERROR; 67399026Sjulian return (ENOMEM); 67499026Sjulian } 67599026Sjulian bzero(hook, sizeof(*hook)); 67699026Sjulian hook->refs = 1; 67799026Sjulian hook->flags = HK_INVALID; 67899026Sjulian hook->node = node; 67999026Sjulian node->refs++; /* each hook counts as a reference */ 68099026Sjulian 68199026Sjulian /* Check if the node type code has something to say about it */ 68299026Sjulian if (node->type->newhook != NULL) 68399026Sjulian if ((error = (*node->type->newhook)(node, hook, name)) != 0) 68499026Sjulian goto fail; 68599026Sjulian 68699026Sjulian /* 68799026Sjulian * The 'type' agrees so far, so go ahead and link it in. 68899026Sjulian * We'll ask again later when we actually connect the hooks. 68999026Sjulian */ 69099026Sjulian LIST_INSERT_HEAD(&node->hooks, hook, hooks); 69199026Sjulian node->numhooks++; 69299026Sjulian 69399026Sjulian /* Set hook name */ 69499026Sjulian MALLOC(hook->name, char *, strlen(name) + 1, M_NETGRAPH, M_WAITOK); 695102950Sdavidxu if (hook->name == NULL) { 69699026Sjulian error = ENOMEM; 69799026Sjulian LIST_REMOVE(hook, hooks); 69899026Sjulian node->numhooks--; 699100648Sjulianfail: 700100648Sjulian hook->node = NULL; 701100646Sjulian ng_unref(node); 702100646Sjulian ng_unref_hook(hook); /* this frees the hook */ 70399026Sjulian return (error); 704100648Sjulian } 70599026Sjulian strcpy(hook->name, name); 70699026Sjulian if (hookp) 707100648Sjulian *hookp = hook; 70899026Sjulian return (error); 70999026Sjulian} 71099026Sjulian 71199026Sjulian/* 71299026Sjulian * Connect a pair of hooks. Only used internally. 713102950Sdavidxu */ 71499026Sjulianstatic int 71599026Sjulianng_connect(hook_p hook1, hook_p hook2) 71699026Sjulian{ 71799026Sjulian int error; 71899026Sjulian 71999026Sjulian hook1->peer = hook2; 72099026Sjulian hook2->peer = hook1; 72199026Sjulian 72299026Sjulian /* Give each node the opportunity to veto the impending connection */ 72399026Sjulian if (hook1->node->type->connect) { 72499026Sjulian if ((error = (*hook1->node->type->connect) (hook1))) { 72599026Sjulian ng_destroy_hook(hook1); /* also zaps hook2 */ 72699026Sjulian return (error); 72799026Sjulian } 72899026Sjulian } 72999026Sjulian if (hook2->node->type->connect) { 73099026Sjulian if ((error = (*hook2->node->type->connect) (hook2))) { 73199026Sjulian ng_destroy_hook(hook2); /* also zaps hook1 */ 73299026Sjulian return (error); 73399026Sjulian } 73499026Sjulian } 735102238Sjulian hook1->flags &= ~HK_INVALID; 736102950Sdavidxu hook2->flags &= ~HK_INVALID; 737102238Sjulian return (0); 738102238Sjulian} 739102238Sjulian 740102238Sjulian/* 741102238Sjulian * Find a hook 742102238Sjulian * 743102238Sjulian * Node types may supply their own optimized routines for finding 744102238Sjulian * hooks. If none is supplied, we just do a linear search. 745103055Sjulian */ 746102238Sjulianhook_p 74799026Sjulianng_findhook(node_p node, const char *name) 74899026Sjulian{ 74999026Sjulian hook_p hook; 75099026Sjulian 75199026Sjulian if (node->type->findhook != NULL) 752102950Sdavidxu return (*node->type->findhook)(node, name); 753100632Sjulian LIST_FOREACH(hook, &node->hooks, hooks) { 754100632Sjulian if (hook->name != NULL && strcmp(hook->name, name) == 0) 755100632Sjulian return (hook); 756100653Sjulian } 757100632Sjulian return (NULL); 758100632Sjulian} 759100632Sjulian 760100594Sjulian/* 76199026Sjulian * Destroy a hook 76299026Sjulian * 76399026Sjulian * As hooks are always attached, this really destroys two hooks. 76499026Sjulian * The one given, and the one attached to it. Disconnect the hooks 76599026Sjulian * from each other first. 76699026Sjulian */ 76799026Sjulianvoid 768102898Sdavidxung_destroy_hook(hook_p hook) 769102898Sdavidxu{ 770102898Sdavidxu hook_p peer = hook->peer; 771102898Sdavidxu 772102898Sdavidxu hook->flags |= HK_INVALID; /* as soon as possible */ 773102898Sdavidxu if (peer) { 774102898Sdavidxu peer->flags |= HK_INVALID; /* as soon as possible */ 775102898Sdavidxu hook->peer = NULL; 776102898Sdavidxu peer->peer = NULL; 777102898Sdavidxu ng_disconnect_hook(peer); 778102898Sdavidxu } 779102898Sdavidxu ng_disconnect_hook(hook); 780102898Sdavidxu} 781102898Sdavidxu 782102898Sdavidxu/* 783102898Sdavidxu * Notify the node of the hook's demise. This may result in more actions 784102898Sdavidxu * (e.g. shutdown) but we don't do that ourselves and don't know what 785102898Sdavidxu * happens there. If there is no appropriate handler, then just remove it 786102898Sdavidxu * (and decrement the reference count of it's node which in turn might 787102898Sdavidxu * make something happen). 788102898Sdavidxu */ 789102898Sdavidxustatic void 790102898Sdavidxung_disconnect_hook(hook_p hook) 791102898Sdavidxu{ 792102898Sdavidxu node_p node = hook->node; 793102898Sdavidxu 794102898Sdavidxu /* 795102898Sdavidxu * Remove the hook from the node's list to avoid possible recursion 796102898Sdavidxu * in case the disconnection results in node shutdown. 797102898Sdavidxu */ 79899026Sjulian LIST_REMOVE(hook, hooks); 79999026Sjulian node->numhooks--; 80099026Sjulian if (node->type->disconnect) { 80199026Sjulian /* 80299026Sjulian * The type handler may elect to destroy the peer so don't 80399026Sjulian * trust its existance after this point. 80499026Sjulian */ 80599026Sjulian (*node->type->disconnect) (hook); 806100646Sjulian } 80799026Sjulian ng_unref(node); /* might be the last reference */ 80899026Sjulian if (hook->name) 80999026Sjulian FREE(hook->name, M_NETGRAPH); 810102898Sdavidxu hook->node = NULL; /* may still be referenced elsewhere */ 81199026Sjulian ng_unref_hook(hook); 812102950Sdavidxu} 81399026Sjulian 81499026Sjulian/* 81599026Sjulian * Take two hooks on a node and merge the connection so that the given node 81699026Sjulian * is effectively bypassed. 81799026Sjulian */ 81899026Sjulianint 819102898Sdavidxung_bypass(hook_p hook1, hook_p hook2) 82099026Sjulian{ 82199026Sjulian if (hook1->node != hook2->node) 82299026Sjulian return (EINVAL); 82399026Sjulian hook1->peer->peer = hook2->peer; 82499026Sjulian hook2->peer->peer = hook1->peer; 82599026Sjulian 82699026Sjulian /* XXX If we ever cache methods on hooks update them as well */ 82799026Sjulian hook1->peer = NULL; 82899026Sjulian hook2->peer = NULL; 82999026Sjulian ng_destroy_hook(hook1); 83099026Sjulian ng_destroy_hook(hook2); 83199026Sjulian return (0); 832102950Sdavidxu} 83399026Sjulian 834102292Sjulian/* 835102292Sjulian * Install a new netgraph type 836102292Sjulian */ 837102292Sjulianint 838102292Sjulianng_newtype(struct ng_type *tp) 839102292Sjulian{ 840102292Sjulian const size_t namelen = strlen(tp->name); 841102292Sjulian 842102292Sjulian /* Check version and type name fields */ 843102292Sjulian if (tp->version != NG_VERSION || namelen == 0 || namelen > NG_TYPELEN) { 844102292Sjulian TRAP_ERROR; 845102292Sjulian return (EINVAL); 846102292Sjulian } 847102292Sjulian 848102292Sjulian /* Check for name collision */ 84999026Sjulian if (ng_findtype(tp->name) != NULL) { 85099026Sjulian TRAP_ERROR; 851102292Sjulian return (EEXIST); 852 } 853 854 /* Link in new type */ 855 LIST_INSERT_HEAD(&typelist, tp, types); 856 tp->refs = 0; 857 return (0); 858} 859 860/* 861 * Look for a type of the name given 862 */ 863struct ng_type * 864ng_findtype(const char *typename) 865{ 866 struct ng_type *type; 867 868 LIST_FOREACH(type, &typelist, types) { 869 if (strcmp(type->name, typename) == 0) 870 break; 871 } 872 return (type); 873} 874 875 876/************************************************************************ 877 Composite routines 878************************************************************************/ 879 880/* 881 * Make a peer and connect. The order is arranged to minimise 882 * the work needed to back out in case of error. 883 */ 884int 885ng_mkpeer(node_p node, const char *name, const char *name2, char *type) 886{ 887 node_p node2; 888 hook_p hook; 889 hook_p hook2; 890 int error; 891 892 if ((error = ng_add_hook(node, name, &hook))) 893 return (error); 894 if ((error = ng_make_node(type, &node2))) { 895 ng_destroy_hook(hook); 896 return (error); 897 } 898 if ((error = ng_add_hook(node2, name2, &hook2))) { 899 ng_rmnode(node2); 900 ng_destroy_hook(hook); 901 return (error); 902 } 903 904 /* 905 * Actually link the two hooks together.. on failure they are 906 * destroyed so we don't have to do that here. 907 */ 908 if ((error = ng_connect(hook, hook2))) 909 ng_rmnode(node2); 910 return (error); 911} 912 913/* 914 * Connect two nodes using the specified hooks 915 */ 916int 917ng_con_nodes(node_p node, const char *name, node_p node2, const char *name2) 918{ 919 int error; 920 hook_p hook; 921 hook_p hook2; 922 923 if ((error = ng_add_hook(node, name, &hook))) 924 return (error); 925 if ((error = ng_add_hook(node2, name2, &hook2))) { 926 ng_destroy_hook(hook); 927 return (error); 928 } 929 return (ng_connect(hook, hook2)); 930} 931 932/* 933 * Parse and verify a string of the form: <NODE:><PATH> 934 * 935 * Such a string can refer to a specific node or a specific hook 936 * on a specific node, depending on how you look at it. In the 937 * latter case, the PATH component must not end in a dot. 938 * 939 * Both <NODE:> and <PATH> are optional. The <PATH> is a string 940 * of hook names separated by dots. This breaks out the original 941 * string, setting *nodep to "NODE" (or NULL if none) and *pathp 942 * to "PATH" (or NULL if degenerate). Also, *hookp will point to 943 * the final hook component of <PATH>, if any, otherwise NULL. 944 * 945 * This returns -1 if the path is malformed. The char ** are optional. 946 */ 947 948int 949ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 950{ 951 char *node, *path, *hook; 952 int k; 953 954 /* 955 * Extract absolute NODE, if any 956 */ 957 for (path = addr; *path && *path != ':'; path++); 958 if (*path) { 959 node = addr; /* Here's the NODE */ 960 *path++ = '\0'; /* Here's the PATH */ 961 962 /* Node name must not be empty */ 963 if (!*node) 964 return -1; 965 966 /* A name of "." is OK; otherwise '.' not allowed */ 967 if (strcmp(node, ".") != 0) { 968 for (k = 0; node[k]; k++) 969 if (node[k] == '.') 970 return -1; 971 } 972 } else { 973 node = NULL; /* No absolute NODE */ 974 path = addr; /* Here's the PATH */ 975 } 976 977 /* Snoop for illegal characters in PATH */ 978 for (k = 0; path[k]; k++) 979 if (path[k] == ':') 980 return -1; 981 982 /* Check for no repeated dots in PATH */ 983 for (k = 0; path[k]; k++) 984 if (path[k] == '.' && path[k + 1] == '.') 985 return -1; 986 987 /* Remove extra (degenerate) dots from beginning or end of PATH */ 988 if (path[0] == '.') 989 path++; 990 if (*path && path[strlen(path) - 1] == '.') 991 path[strlen(path) - 1] = 0; 992 993 /* If PATH has a dot, then we're not talking about a hook */ 994 if (*path) { 995 for (hook = path, k = 0; path[k]; k++) 996 if (path[k] == '.') { 997 hook = NULL; 998 break; 999 } 1000 } else 1001 path = hook = NULL; 1002 1003 /* Done */ 1004 if (nodep) 1005 *nodep = node; 1006 if (pathp) 1007 *pathp = path; 1008 if (hookp) 1009 *hookp = hook; 1010 return (0); 1011} 1012 1013/* 1014 * Given a path, which may be absolute or relative, and a starting node, 1015 * return the destination node. Compute the "return address" if desired. 1016 */ 1017int 1018ng_path2node(node_p here, const char *address, node_p *destp, char **rtnp, 1019 hook_p *lasthook) 1020{ 1021 const node_p start = here; 1022 char fullpath[NG_PATHLEN + 1]; 1023 char *nodename, *path, pbuf[2]; 1024 node_p node; 1025 char *cp; 1026 hook_p hook = NULL; 1027 1028 /* Initialize */ 1029 if (rtnp) 1030 *rtnp = NULL; 1031 if (destp == NULL) 1032 return EINVAL; 1033 *destp = NULL; 1034 1035 /* Make a writable copy of address for ng_path_parse() */ 1036 strncpy(fullpath, address, sizeof(fullpath) - 1); 1037 fullpath[sizeof(fullpath) - 1] = '\0'; 1038 1039 /* Parse out node and sequence of hooks */ 1040 if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 1041 TRAP_ERROR; 1042 return EINVAL; 1043 } 1044 if (path == NULL) { 1045 pbuf[0] = '.'; /* Needs to be writable */ 1046 pbuf[1] = '\0'; 1047 path = pbuf; 1048 } 1049 1050 /* For an absolute address, jump to the starting node */ 1051 if (nodename) { 1052 node = ng_findname(here, nodename); 1053 if (node == NULL) { 1054 TRAP_ERROR; 1055 return (ENOENT); 1056 } 1057 } else 1058 node = here; 1059 1060 /* Now follow the sequence of hooks */ 1061 for (cp = path; node != NULL && *cp != '\0'; ) { 1062 char *segment; 1063 1064 /* 1065 * Break out the next path segment. Replace the dot we just 1066 * found with a NUL; "cp" points to the next segment (or the 1067 * NUL at the end). 1068 */ 1069 for (segment = cp; *cp != '\0'; cp++) { 1070 if (*cp == '.') { 1071 *cp++ = '\0'; 1072 break; 1073 } 1074 } 1075 1076 /* Empty segment */ 1077 if (*segment == '\0') 1078 continue; 1079 1080 /* We have a segment, so look for a hook by that name */ 1081 hook = ng_findhook(node, segment); 1082 1083 /* Can't get there from here... */ 1084 if (hook == NULL 1085 || hook->peer == NULL 1086 || (hook->flags & HK_INVALID) != 0) { 1087 TRAP_ERROR; 1088 return (ENOENT); 1089 } 1090 1091 /* Hop on over to the next node */ 1092 node = hook->peer->node; 1093 } 1094 1095 /* If node somehow missing, fail here (probably this is not needed) */ 1096 if (node == NULL) { 1097 TRAP_ERROR; 1098 return (ENXIO); 1099 } 1100 1101 /* Now compute return address, i.e., the path to the sender */ 1102 if (rtnp != NULL) { 1103 MALLOC(*rtnp, char *, NG_NODELEN + 2, M_NETGRAPH, M_WAITOK); 1104 if (*rtnp == NULL) { 1105 TRAP_ERROR; 1106 return (ENOMEM); 1107 } 1108 if (start->name != NULL) 1109 sprintf(*rtnp, "%s:", start->name); 1110 else 1111 sprintf(*rtnp, "[%x]:", ng_node2ID(start)); 1112 } 1113 1114 /* Done */ 1115 *destp = node; 1116 if (lasthook && hook) 1117 *lasthook = hook->peer; 1118 return (0); 1119} 1120 1121/* 1122 * Call the appropriate message handler for the object. 1123 * It is up to the message handler to free the message. 1124 * If it's a generic message, handle it generically, otherwise 1125 * call the type's message handler (if it exists) 1126 */ 1127 1128#define CALL_MSG_HANDLER(error, node, msg, retaddr, resp, hook) \ 1129do { \ 1130 if((msg)->header.typecookie == NGM_GENERIC_COOKIE) { \ 1131 (error) = ng_generic_msg((node), (msg), \ 1132 (retaddr), (resp), (hook)); \ 1133 } else { \ 1134 if ((node)->type->rcvmsg != NULL) { \ 1135 (error) = (*(node)->type->rcvmsg)((node), \ 1136 (msg), (retaddr), (resp), (hook)); \ 1137 } else { \ 1138 TRAP_ERROR; \ 1139 FREE((msg), M_NETGRAPH); \ 1140 (error) = EINVAL; \ 1141 } \ 1142 } \ 1143} while (0) 1144 1145 1146/* 1147 * Send a control message to a node 1148 */ 1149int 1150ng_send_msg(node_p here, struct ng_mesg *msg, const char *address, 1151 struct ng_mesg **rptr) 1152{ 1153 node_p dest = NULL; 1154 char *retaddr = NULL; 1155 int error; 1156 hook_p lasthook; 1157 1158 /* Find the target node */ 1159 error = ng_path2node(here, address, &dest, &retaddr, &lasthook); 1160 if (error) { 1161 FREE(msg, M_NETGRAPH); 1162 return error; 1163 } 1164 1165 /* Make sure the resp field is null before we start */ 1166 if (rptr != NULL) 1167 *rptr = NULL; 1168 1169 CALL_MSG_HANDLER(error, dest, msg, retaddr, rptr, lasthook); 1170 1171 /* Make sure that if there is a response, it has the RESP bit set */ 1172 if ((error == 0) && rptr && *rptr) 1173 (*rptr)->header.flags |= NGF_RESP; 1174 1175 /* 1176 * If we had a return address it is up to us to free it. They should 1177 * have taken a copy if they needed to make a delayed response. 1178 */ 1179 if (retaddr) 1180 FREE(retaddr, M_NETGRAPH); 1181 return (error); 1182} 1183 1184/* 1185 * Implement the 'generic' control messages 1186 */ 1187static int 1188ng_generic_msg(node_p here, struct ng_mesg *msg, const char *retaddr, 1189 struct ng_mesg **resp, hook_p lasthook) 1190{ 1191 int error = 0; 1192 1193 if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 1194 TRAP_ERROR; 1195 FREE(msg, M_NETGRAPH); 1196 return (EINVAL); 1197 } 1198 switch (msg->header.cmd) { 1199 case NGM_SHUTDOWN: 1200 ng_rmnode(here); 1201 break; 1202 case NGM_MKPEER: 1203 { 1204 struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 1205 1206 if (msg->header.arglen != sizeof(*mkp)) { 1207 TRAP_ERROR; 1208 return (EINVAL); 1209 } 1210 mkp->type[sizeof(mkp->type) - 1] = '\0'; 1211 mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 1212 mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 1213 error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 1214 break; 1215 } 1216 case NGM_CONNECT: 1217 { 1218 struct ngm_connect *const con = 1219 (struct ngm_connect *) msg->data; 1220 node_p node2; 1221 1222 if (msg->header.arglen != sizeof(*con)) { 1223 TRAP_ERROR; 1224 return (EINVAL); 1225 } 1226 con->path[sizeof(con->path) - 1] = '\0'; 1227 con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 1228 con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 1229 error = ng_path2node(here, con->path, &node2, NULL, NULL); 1230 if (error) 1231 break; 1232 error = ng_con_nodes(here, con->ourhook, node2, con->peerhook); 1233 break; 1234 } 1235 case NGM_NAME: 1236 { 1237 struct ngm_name *const nam = (struct ngm_name *) msg->data; 1238 1239 if (msg->header.arglen != sizeof(*nam)) { 1240 TRAP_ERROR; 1241 return (EINVAL); 1242 } 1243 nam->name[sizeof(nam->name) - 1] = '\0'; 1244 error = ng_name_node(here, nam->name); 1245 break; 1246 } 1247 case NGM_RMHOOK: 1248 { 1249 struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 1250 hook_p hook; 1251 1252 if (msg->header.arglen != sizeof(*rmh)) { 1253 TRAP_ERROR; 1254 return (EINVAL); 1255 } 1256 rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 1257 if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 1258 ng_destroy_hook(hook); 1259 break; 1260 } 1261 case NGM_NODEINFO: 1262 { 1263 struct nodeinfo *ni; 1264 struct ng_mesg *rp; 1265 1266 /* Get response struct */ 1267 if (resp == NULL) { 1268 error = EINVAL; 1269 break; 1270 } 1271 NG_MKRESPONSE(rp, msg, sizeof(*ni), M_NOWAIT); 1272 if (rp == NULL) { 1273 error = ENOMEM; 1274 break; 1275 } 1276 1277 /* Fill in node info */ 1278 ni = (struct nodeinfo *) rp->data; 1279 if (here->name != NULL) 1280 strncpy(ni->name, here->name, NG_NODELEN); 1281 strncpy(ni->type, here->type->name, NG_TYPELEN); 1282 ni->id = ng_node2ID(here); 1283 ni->hooks = here->numhooks; 1284 *resp = rp; 1285 break; 1286 } 1287 case NGM_LISTHOOKS: 1288 { 1289 const int nhooks = here->numhooks; 1290 struct hooklist *hl; 1291 struct nodeinfo *ni; 1292 struct ng_mesg *rp; 1293 hook_p hook; 1294 1295 /* Get response struct */ 1296 if (resp == NULL) { 1297 error = EINVAL; 1298 break; 1299 } 1300 NG_MKRESPONSE(rp, msg, sizeof(*hl) 1301 + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 1302 if (rp == NULL) { 1303 error = ENOMEM; 1304 break; 1305 } 1306 hl = (struct hooklist *) rp->data; 1307 ni = &hl->nodeinfo; 1308 1309 /* Fill in node info */ 1310 if (here->name) 1311 strncpy(ni->name, here->name, NG_NODELEN); 1312 strncpy(ni->type, here->type->name, NG_TYPELEN); 1313 ni->id = ng_node2ID(here); 1314 1315 /* Cycle through the linked list of hooks */ 1316 ni->hooks = 0; 1317 LIST_FOREACH(hook, &here->hooks, hooks) { 1318 struct linkinfo *const link = &hl->link[ni->hooks]; 1319 1320 if (ni->hooks >= nhooks) { 1321 log(LOG_ERR, "%s: number of %s changed\n", 1322 __FUNCTION__, "hooks"); 1323 break; 1324 } 1325 if ((hook->flags & HK_INVALID) != 0) 1326 continue; 1327 strncpy(link->ourhook, hook->name, NG_HOOKLEN); 1328 strncpy(link->peerhook, hook->peer->name, NG_HOOKLEN); 1329 if (hook->peer->node->name != NULL) 1330 strncpy(link->nodeinfo.name, 1331 hook->peer->node->name, NG_NODELEN); 1332 strncpy(link->nodeinfo.type, 1333 hook->peer->node->type->name, NG_TYPELEN); 1334 link->nodeinfo.id = ng_node2ID(hook->peer->node); 1335 link->nodeinfo.hooks = hook->peer->node->numhooks; 1336 ni->hooks++; 1337 } 1338 *resp = rp; 1339 break; 1340 } 1341 1342 case NGM_LISTNAMES: 1343 case NGM_LISTNODES: 1344 { 1345 const int unnamed = (msg->header.cmd == NGM_LISTNODES); 1346 struct namelist *nl; 1347 struct ng_mesg *rp; 1348 node_p node; 1349 int num = 0; 1350 1351 if (resp == NULL) { 1352 error = EINVAL; 1353 break; 1354 } 1355 1356 /* Count number of nodes */ 1357 LIST_FOREACH(node, &nodelist, nodes) { 1358 if (unnamed || node->name != NULL) 1359 num++; 1360 } 1361 1362 /* Get response struct */ 1363 if (resp == NULL) { 1364 error = EINVAL; 1365 break; 1366 } 1367 NG_MKRESPONSE(rp, msg, sizeof(*nl) 1368 + (num * sizeof(struct nodeinfo)), M_NOWAIT); 1369 if (rp == NULL) { 1370 error = ENOMEM; 1371 break; 1372 } 1373 nl = (struct namelist *) rp->data; 1374 1375 /* Cycle through the linked list of nodes */ 1376 nl->numnames = 0; 1377 LIST_FOREACH(node, &nodelist, nodes) { 1378 struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; 1379 1380 if (nl->numnames >= num) { 1381 log(LOG_ERR, "%s: number of %s changed\n", 1382 __FUNCTION__, "nodes"); 1383 break; 1384 } 1385 if ((node->flags & NG_INVALID) != 0) 1386 continue; 1387 if (!unnamed && node->name == NULL) 1388 continue; 1389 if (node->name != NULL) 1390 strncpy(np->name, node->name, NG_NODELEN); 1391 strncpy(np->type, node->type->name, NG_TYPELEN); 1392 np->id = ng_node2ID(node); 1393 np->hooks = node->numhooks; 1394 nl->numnames++; 1395 } 1396 *resp = rp; 1397 break; 1398 } 1399 1400 case NGM_LISTTYPES: 1401 { 1402 struct typelist *tl; 1403 struct ng_mesg *rp; 1404 struct ng_type *type; 1405 int num = 0; 1406 1407 if (resp == NULL) { 1408 error = EINVAL; 1409 break; 1410 } 1411 1412 /* Count number of types */ 1413 LIST_FOREACH(type, &typelist, types) 1414 num++; 1415 1416 /* Get response struct */ 1417 if (resp == NULL) { 1418 error = EINVAL; 1419 break; 1420 } 1421 NG_MKRESPONSE(rp, msg, sizeof(*tl) 1422 + (num * sizeof(struct typeinfo)), M_NOWAIT); 1423 if (rp == NULL) { 1424 error = ENOMEM; 1425 break; 1426 } 1427 tl = (struct typelist *) rp->data; 1428 1429 /* Cycle through the linked list of types */ 1430 tl->numtypes = 0; 1431 LIST_FOREACH(type, &typelist, types) { 1432 struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 1433 1434 if (tl->numtypes >= num) { 1435 log(LOG_ERR, "%s: number of %s changed\n", 1436 __FUNCTION__, "types"); 1437 break; 1438 } 1439 strncpy(tp->type_name, type->name, NG_TYPELEN); 1440 tp->numnodes = type->refs; 1441 tl->numtypes++; 1442 } 1443 *resp = rp; 1444 break; 1445 } 1446 1447 case NGM_BINARY2ASCII: 1448 { 1449 int bufSize = 2000; /* XXX hard coded constant */ 1450 const struct ng_parse_type *argstype; 1451 const struct ng_cmdlist *c; 1452 struct ng_mesg *rp, *binary, *ascii; 1453 1454 /* Data area must contain a valid netgraph message */ 1455 binary = (struct ng_mesg *)msg->data; 1456 if (msg->header.arglen < sizeof(struct ng_mesg) 1457 || msg->header.arglen - sizeof(struct ng_mesg) 1458 < binary->header.arglen) { 1459 error = EINVAL; 1460 break; 1461 } 1462 1463 /* Get a response message with lots of room */ 1464 NG_MKRESPONSE(rp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 1465 if (rp == NULL) { 1466 error = ENOMEM; 1467 break; 1468 } 1469 ascii = (struct ng_mesg *)rp->data; 1470 1471 /* Copy binary message header to response message payload */ 1472 bcopy(binary, ascii, sizeof(*binary)); 1473 1474 /* Find command by matching typecookie and command number */ 1475 for (c = here->type->cmdlist; 1476 c != NULL && c->name != NULL; c++) { 1477 if (binary->header.typecookie == c->cookie 1478 && binary->header.cmd == c->cmd) 1479 break; 1480 } 1481 if (c == NULL || c->name == NULL) { 1482 for (c = ng_generic_cmds; c->name != NULL; c++) { 1483 if (binary->header.typecookie == c->cookie 1484 && binary->header.cmd == c->cmd) 1485 break; 1486 } 1487 if (c->name == NULL) { 1488 FREE(rp, M_NETGRAPH); 1489 error = ENOSYS; 1490 break; 1491 } 1492 } 1493 1494 /* Convert command name to ASCII */ 1495 snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 1496 "%s", c->name); 1497 1498 /* Convert command arguments to ASCII */ 1499 argstype = (binary->header.flags & NGF_RESP) ? 1500 c->respType : c->mesgType; 1501 if (argstype == NULL) 1502 *ascii->data = '\0'; 1503 else { 1504 if ((error = ng_unparse(argstype, 1505 (u_char *)binary->data, 1506 ascii->data, bufSize)) != 0) { 1507 FREE(rp, M_NETGRAPH); 1508 break; 1509 } 1510 } 1511 1512 /* Return the result as struct ng_mesg plus ASCII string */ 1513 bufSize = strlen(ascii->data) + 1; 1514 ascii->header.arglen = bufSize; 1515 rp->header.arglen = sizeof(*ascii) + bufSize; 1516 *resp = rp; 1517 break; 1518 } 1519 1520 case NGM_ASCII2BINARY: 1521 { 1522 int bufSize = 2000; /* XXX hard coded constant */ 1523 const struct ng_cmdlist *c; 1524 const struct ng_parse_type *argstype; 1525 struct ng_mesg *rp, *ascii, *binary; 1526 int off = 0; 1527 1528 /* Data area must contain at least a struct ng_mesg + '\0' */ 1529 ascii = (struct ng_mesg *)msg->data; 1530 if (msg->header.arglen < sizeof(*ascii) + 1 1531 || ascii->header.arglen < 1 1532 || msg->header.arglen 1533 < sizeof(*ascii) + ascii->header.arglen) { 1534 error = EINVAL; 1535 break; 1536 } 1537 ascii->data[ascii->header.arglen - 1] = '\0'; 1538 1539 /* Get a response message with lots of room */ 1540 NG_MKRESPONSE(rp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 1541 if (rp == NULL) { 1542 error = ENOMEM; 1543 break; 1544 } 1545 binary = (struct ng_mesg *)rp->data; 1546 1547 /* Copy ASCII message header to response message payload */ 1548 bcopy(ascii, binary, sizeof(*ascii)); 1549 1550 /* Find command by matching ASCII command string */ 1551 for (c = here->type->cmdlist; 1552 c != NULL && c->name != NULL; c++) { 1553 if (strcmp(ascii->header.cmdstr, c->name) == 0) 1554 break; 1555 } 1556 if (c == NULL || c->name == NULL) { 1557 for (c = ng_generic_cmds; c->name != NULL; c++) { 1558 if (strcmp(ascii->header.cmdstr, c->name) == 0) 1559 break; 1560 } 1561 if (c->name == NULL) { 1562 FREE(rp, M_NETGRAPH); 1563 error = ENOSYS; 1564 break; 1565 } 1566 } 1567 1568 /* Convert command name to binary */ 1569 binary->header.cmd = c->cmd; 1570 binary->header.typecookie = c->cookie; 1571 1572 /* Convert command arguments to binary */ 1573 argstype = (binary->header.flags & NGF_RESP) ? 1574 c->respType : c->mesgType; 1575 if (argstype == NULL) 1576 bufSize = 0; 1577 else { 1578 if ((error = ng_parse(argstype, ascii->data, 1579 &off, (u_char *)binary->data, &bufSize)) != 0) { 1580 FREE(rp, M_NETGRAPH); 1581 break; 1582 } 1583 } 1584 1585 /* Return the result */ 1586 binary->header.arglen = bufSize; 1587 rp->header.arglen = sizeof(*binary) + bufSize; 1588 *resp = rp; 1589 break; 1590 } 1591 1592 case NGM_TEXT_STATUS: 1593 /* 1594 * This one is tricky as it passes the command down to the 1595 * actual node, even though it is a generic type command. 1596 * This means we must assume that the msg is already freed 1597 * when control passes back to us. 1598 */ 1599 if (resp == NULL) { 1600 error = EINVAL; 1601 break; 1602 } 1603 if (here->type->rcvmsg != NULL) 1604 return((*here->type->rcvmsg)(here, msg, retaddr, 1605 resp, lasthook)); 1606 /* Fall through if rcvmsg not supported */ 1607 default: 1608 TRAP_ERROR; 1609 error = EINVAL; 1610 } 1611 FREE(msg, M_NETGRAPH); 1612 return (error); 1613} 1614 1615/* 1616 * Send a data packet to a node. If the recipient has no 1617 * 'receive data' method, then silently discard the packet. 1618 */ 1619int 1620ng_send_data(hook_p hook, struct mbuf *m, meta_p meta, 1621 struct mbuf **ret_m, meta_p *ret_meta) 1622{ 1623 ng_rcvdata_t *rcvdata; 1624 int error; 1625 1626 CHECK_DATA_MBUF(m); 1627 if (hook && (hook->flags & HK_INVALID) == 0) { 1628 rcvdata = hook->peer->node->type->rcvdata; 1629 if (rcvdata != NULL) 1630 error = (*rcvdata)(hook->peer, m, meta, 1631 ret_m, ret_meta); 1632 else { 1633 error = 0; 1634 NG_FREE_DATA(m, meta); 1635 } 1636 } else { 1637 TRAP_ERROR; 1638 error = ENOTCONN; 1639 NG_FREE_DATA(m, meta); 1640 } 1641 return (error); 1642} 1643 1644/* 1645 * Send a queued data packet to a node. If the recipient has no 1646 * 'receive queued data' method, then try the 'receive data' method above. 1647 */ 1648int 1649ng_send_dataq(hook_p hook, struct mbuf *m, meta_p meta, 1650 struct mbuf **ret_m, meta_p *ret_meta) 1651{ 1652 ng_rcvdata_t *rcvdata; 1653 int error; 1654 1655 CHECK_DATA_MBUF(m); 1656 if (hook && (hook->flags & HK_INVALID) == 0) { 1657 rcvdata = hook->peer->node->type->rcvdataq; 1658 if (rcvdata != NULL) 1659 error = (*rcvdata)(hook->peer, m, meta, 1660 ret_m, ret_meta); 1661 else { 1662 rcvdata = hook->peer->node->type->rcvdata; 1663 if (rcvdata != NULL) { 1664 error = (*rcvdata)(hook->peer, m, meta, 1665 ret_m, ret_meta); 1666 } else { 1667 error = 0; 1668 NG_FREE_DATA(m, meta); 1669 } 1670 } 1671 } else { 1672 TRAP_ERROR; 1673 error = ENOTCONN; 1674 NG_FREE_DATA(m, meta); 1675 } 1676 return (error); 1677} 1678 1679/* 1680 * Copy a 'meta'. 1681 * 1682 * Returns new meta, or NULL if original meta is NULL or ENOMEM. 1683 */ 1684meta_p 1685ng_copy_meta(meta_p meta) 1686{ 1687 meta_p meta2; 1688 1689 if (meta == NULL) 1690 return (NULL); 1691 MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH, M_NOWAIT); 1692 if (meta2 == NULL) 1693 return (NULL); 1694 meta2->allocated_len = meta->used_len; 1695 bcopy(meta, meta2, meta->used_len); 1696 return (meta2); 1697} 1698 1699/************************************************************************ 1700 Module routines 1701************************************************************************/ 1702 1703/* 1704 * Handle the loading/unloading of a netgraph node type module 1705 */ 1706int 1707ng_mod_event(module_t mod, int event, void *data) 1708{ 1709 struct ng_type *const type = data; 1710 int s, error = 0; 1711 1712 switch (event) { 1713 case MOD_LOAD: 1714 1715 /* Register new netgraph node type */ 1716 s = splnet(); 1717 if ((error = ng_newtype(type)) != 0) { 1718 splx(s); 1719 break; 1720 } 1721 1722 /* Call type specific code */ 1723 if (type->mod_event != NULL) 1724 if ((error = (*type->mod_event)(mod, event, data)) != 0) 1725 LIST_REMOVE(type, types); 1726 splx(s); 1727 break; 1728 1729 case MOD_UNLOAD: 1730 s = splnet(); 1731 if (type->refs != 0) /* make sure no nodes exist! */ 1732 error = EBUSY; 1733 else { 1734 if (type->mod_event != NULL) { /* check with type */ 1735 error = (*type->mod_event)(mod, event, data); 1736 if (error != 0) { /* type refuses.. */ 1737 splx(s); 1738 break; 1739 } 1740 } 1741 LIST_REMOVE(type, types); 1742 } 1743 splx(s); 1744 break; 1745 1746 default: 1747 if (type->mod_event != NULL) 1748 error = (*type->mod_event)(mod, event, data); 1749 else 1750 error = 0; /* XXX ? */ 1751 break; 1752 } 1753 return (error); 1754} 1755 1756/* 1757 * Handle loading and unloading for this code. 1758 * The only thing we need to link into is the NETISR strucure. 1759 */ 1760static int 1761ngb_mod_event(module_t mod, int event, void *data) 1762{ 1763 int s, error = 0; 1764 1765 switch (event) { 1766 case MOD_LOAD: 1767 /* Register line discipline */ 1768 s = splimp(); 1769 error = register_netisr(NETISR_NETGRAPH, ngintr); 1770 splx(s); 1771 break; 1772 case MOD_UNLOAD: 1773 /* You cant unload it because an interface may be using it. */ 1774 error = EBUSY; 1775 break; 1776 default: 1777 error = EOPNOTSUPP; 1778 break; 1779 } 1780 return (error); 1781} 1782 1783static moduledata_t netgraph_mod = { 1784 "netgraph", 1785 ngb_mod_event, 1786 (NULL) 1787}; 1788DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 1789 1790/************************************************************************ 1791 Queueing routines 1792************************************************************************/ 1793 1794/* The structure for queueing across ISR switches */ 1795struct ng_queue_entry { 1796 u_long flags; 1797 struct ng_queue_entry *next; 1798 union { 1799 struct { 1800 hook_p da_hook; /* target hook */ 1801 struct mbuf *da_m; 1802 meta_p da_meta; 1803 } data; 1804 struct { 1805 struct ng_mesg *msg_msg; 1806 node_p msg_node; 1807 void *msg_retaddr; 1808 hook_p msg_lasthook; 1809 } msg; 1810 } body; 1811}; 1812#define NGQF_DATA 0x01 /* the queue element is data */ 1813#define NGQF_MESG 0x02 /* the queue element is a message */ 1814 1815static struct ng_queue_entry *ngqbase; /* items to be unqueued */ 1816static struct ng_queue_entry *ngqlast; /* last item queued */ 1817static const int ngqroom = 64; /* max items to queue */ 1818static int ngqsize; /* number of items in queue */ 1819 1820static struct ng_queue_entry *ngqfree; /* free ones */ 1821static const int ngqfreemax = 16;/* cache at most this many */ 1822static int ngqfreesize; /* number of cached entries */ 1823 1824/* 1825 * Get a queue entry 1826 */ 1827static struct ng_queue_entry * 1828ng_getqblk(void) 1829{ 1830 register struct ng_queue_entry *q; 1831 int s; 1832 1833 /* Could be guarding against tty ints or whatever */ 1834 s = splhigh(); 1835 1836 /* Try get a cached queue block, or else allocate a new one */ 1837 if ((q = ngqfree) == NULL) { 1838 splx(s); 1839 if (ngqsize < ngqroom) { /* don't worry about races */ 1840 MALLOC(q, struct ng_queue_entry *, 1841 sizeof(*q), M_NETGRAPH, M_NOWAIT); 1842 } 1843 } else { 1844 ngqfree = q->next; 1845 ngqfreesize--; 1846 splx(s); 1847 } 1848 return (q); 1849} 1850 1851/* 1852 * Release a queue entry 1853 */ 1854#define RETURN_QBLK(q) \ 1855do { \ 1856 int s; \ 1857 if (ngqfreesize < ngqfreemax) { /* don't worry about races */ \ 1858 s = splhigh(); \ 1859 (q)->next = ngqfree; \ 1860 ngqfree = (q); \ 1861 ngqfreesize++; \ 1862 splx(s); \ 1863 } else { \ 1864 FREE((q), M_NETGRAPH); \ 1865 } \ 1866} while (0) 1867 1868/* 1869 * Running at a raised (but we don't know which) processor priority level, 1870 * put the data onto a queue to be picked up by another PPL (probably splnet) 1871 */ 1872int 1873ng_queue_data(hook_p hook, struct mbuf *m, meta_p meta) 1874{ 1875 struct ng_queue_entry *q; 1876 int s; 1877 1878 if (hook == NULL) { 1879 NG_FREE_DATA(m, meta); 1880 return (0); 1881 } 1882 if ((q = ng_getqblk()) == NULL) { 1883 NG_FREE_DATA(m, meta); 1884 return (ENOBUFS); 1885 } 1886 1887 /* Fill out the contents */ 1888 q->flags = NGQF_DATA; 1889 q->next = NULL; 1890 q->body.data.da_hook = hook; 1891 q->body.data.da_m = m; 1892 q->body.data.da_meta = meta; 1893 hook->refs++; /* don't let it go away while on the queue */ 1894 1895 /* Put it on the queue */ 1896 s = splhigh(); 1897 if (ngqbase) { 1898 ngqlast->next = q; 1899 } else { 1900 ngqbase = q; 1901 } 1902 ngqlast = q; 1903 ngqsize++; 1904 splx(s); 1905 1906 /* Schedule software interrupt to handle it later */ 1907 schednetisr(NETISR_NETGRAPH); 1908 return (0); 1909} 1910 1911/* 1912 * Running at a raised (but we don't know which) processor priority level, 1913 * put the msg onto a queue to be picked up by another PPL (probably splnet) 1914 */ 1915int 1916ng_queue_msg(node_p here, struct ng_mesg *msg, const char *address) 1917{ 1918 register struct ng_queue_entry *q; 1919 int s; 1920 node_p dest = NULL; 1921 char *retaddr = NULL; 1922 int error; 1923 hook_p lasthook = NULL; 1924 1925 /* Find the target node. */ 1926 error = ng_path2node(here, address, &dest, &retaddr, &lasthook); 1927 if (error) { 1928 FREE(msg, M_NETGRAPH); 1929 return (error); 1930 } 1931 if ((q = ng_getqblk()) == NULL) { 1932 FREE(msg, M_NETGRAPH); 1933 if (retaddr) 1934 FREE(retaddr, M_NETGRAPH); 1935 return (ENOBUFS); 1936 } 1937 1938 /* Fill out the contents */ 1939 q->flags = NGQF_MESG; 1940 q->next = NULL; 1941 q->body.msg.msg_node = dest; 1942 q->body.msg.msg_msg = msg; 1943 q->body.msg.msg_retaddr = retaddr; 1944 q->body.msg.msg_lasthook = lasthook; 1945 dest->refs++; /* don't let it go away while on the queue */ 1946 if (lasthook) 1947 lasthook->refs++; /* same for the hook */ 1948 1949 /* Put it on the queue */ 1950 s = splhigh(); 1951 if (ngqbase) { 1952 ngqlast->next = q; 1953 } else { 1954 ngqbase = q; 1955 } 1956 ngqlast = q; 1957 ngqsize++; 1958 splx(s); 1959 1960 /* Schedule software interrupt to handle it later */ 1961 schednetisr(NETISR_NETGRAPH); 1962 return (0); 1963} 1964 1965/* 1966 * Pick an item off the queue, process it, and dispose of the queue entry. 1967 * Should be running at splnet. 1968 */ 1969static void 1970ngintr(void) 1971{ 1972 hook_p hook; 1973 struct ng_queue_entry *ngq; 1974 struct mbuf *m; 1975 meta_p meta; 1976 void *retaddr; 1977 struct ng_mesg *msg; 1978 node_p node; 1979 int error = 0; 1980 int s; 1981 1982 while (1) { 1983 s = splhigh(); 1984 if ((ngq = ngqbase)) { 1985 ngqbase = ngq->next; 1986 ngqsize--; 1987 } 1988 splx(s); 1989 if (ngq == NULL) 1990 return; 1991 switch (ngq->flags) { 1992 case NGQF_DATA: 1993 hook = ngq->body.data.da_hook; 1994 m = ngq->body.data.da_m; 1995 meta = ngq->body.data.da_meta; 1996 RETURN_QBLK(ngq); 1997 NG_SEND_DATAQ(error, hook, m, meta); 1998 ng_unref_hook(hook); 1999 break; 2000 case NGQF_MESG: 2001 node = ngq->body.msg.msg_node; 2002 msg = ngq->body.msg.msg_msg; 2003 retaddr = ngq->body.msg.msg_retaddr; 2004 hook = ngq->body.msg.msg_lasthook; 2005 RETURN_QBLK(ngq); 2006 if (hook) { 2007 if ((hook->refs == 1) 2008 || (hook->flags & HK_INVALID) != 0) { 2009 /* If the hook only has one ref left 2010 then we can't use it */ 2011 ng_unref_hook(hook); 2012 hook = NULL; 2013 } else { 2014 ng_unref_hook(hook); 2015 } 2016 } 2017 /* similarly, if the node is a zombie.. */ 2018 if (node->flags & NG_INVALID) { 2019 FREE(msg, M_NETGRAPH); 2020 } else { 2021 CALL_MSG_HANDLER(error, node, msg, 2022 retaddr, NULL, hook); 2023 } 2024 ng_unref(node); 2025 if (retaddr) 2026 FREE(retaddr, M_NETGRAPH); 2027 break; 2028 default: 2029 RETURN_QBLK(ngq); 2030 } 2031 } 2032} 2033 2034 2035