ng_base.c revision 64510
152419Sjulian 252419Sjulian/* 352419Sjulian * ng_base.c 452419Sjulian * 552419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc. 652419Sjulian * All rights reserved. 752419Sjulian * 852419Sjulian * Subject to the following obligations and disclaimer of warranty, use and 952419Sjulian * redistribution of this software, in source or object code forms, with or 1052419Sjulian * without modifications are expressly permitted by Whistle Communications; 1152419Sjulian * provided, however, that: 1252419Sjulian * 1. Any and all reproductions of the source or object code must include the 1352419Sjulian * copyright notice above and the following disclaimer of warranties; and 1452419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle 1552419Sjulian * Communications, Inc. trademarks, including the mark "WHISTLE 1652419Sjulian * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1752419Sjulian * such appears in the above copyright notice or in the software. 1852419Sjulian * 1952419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 2052419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 2152419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 2252419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2352419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 2452419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2552419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 2652419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2752419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 2852419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 2952419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 3052419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 3152419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 3252419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3352419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3452419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3552419Sjulian * OF SUCH DAMAGE. 3652419Sjulian * 3752419Sjulian * Authors: Julian Elischer <julian@whistle.com> 3852419Sjulian * Archie Cobbs <archie@whistle.com> 3952419Sjulian * 4052419Sjulian * $FreeBSD: head/sys/netgraph/ng_base.c 64510 2000-08-10 22:50:38Z archie $ 4152419Sjulian * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $ 4252419Sjulian */ 4352419Sjulian 4452419Sjulian/* 4552419Sjulian * This file implements the base netgraph code. 4652419Sjulian */ 4752419Sjulian 4852419Sjulian#include <sys/param.h> 4952419Sjulian#include <sys/systm.h> 5052419Sjulian#include <sys/errno.h> 5152419Sjulian#include <sys/kernel.h> 5252419Sjulian#include <sys/malloc.h> 5352419Sjulian#include <sys/syslog.h> 5452419Sjulian#include <sys/linker.h> 5552419Sjulian#include <sys/queue.h> 5652419Sjulian#include <sys/mbuf.h> 5752843Sphk#include <sys/ctype.h> 5852816Sarchie#include <machine/limits.h> 5952419Sjulian 6052419Sjulian#include <net/netisr.h> 6152419Sjulian 6252419Sjulian#include <netgraph/ng_message.h> 6352419Sjulian#include <netgraph/netgraph.h> 6453913Sarchie#include <netgraph/ng_parse.h> 6552419Sjulian 6659756SpeterMODULE_VERSION(netgraph, 1); 6759756Speter 6852419Sjulian/* List of all nodes */ 6960938Sjakestatic LIST_HEAD(, ng_node) nodelist; 7052419Sjulian 7152419Sjulian/* List of installed types */ 7260938Sjakestatic LIST_HEAD(, ng_type) typelist; 7352419Sjulian 7452722Sjulian/* Hash releted definitions */ 7552722Sjulian#define ID_HASH_SIZE 32 /* most systems wont need even this many */ 7660938Sjakestatic LIST_HEAD(, ng_node) ID_hash[ID_HASH_SIZE]; 7752722Sjulian/* Don't nead to initialise them because it's a LIST */ 7852722Sjulian 7952419Sjulian/* Internal functions */ 8052419Sjulianstatic int ng_add_hook(node_p node, const char *name, hook_p * hookp); 8152419Sjulianstatic int ng_connect(hook_p hook1, hook_p hook2); 8252419Sjulianstatic void ng_disconnect_hook(hook_p hook); 8352419Sjulianstatic int ng_generic_msg(node_p here, struct ng_mesg *msg, 8459728Sjulian const char *retaddr, struct ng_mesg ** resp, 8559728Sjulian hook_p hook); 8652722Sjulianstatic ng_ID_t ng_decodeidname(const char *name); 8752419Sjulianstatic int ngb_mod_event(module_t mod, int event, void *data); 8852419Sjulianstatic void ngintr(void); 8952419Sjulian 9052419Sjulian/* Our own netgraph malloc type */ 9152419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); 9252419Sjulian 9352419Sjulian/* Set this to Debugger("X") to catch all errors as they occur */ 9452419Sjulian#ifndef TRAP_ERROR 9552419Sjulian#define TRAP_ERROR 9652419Sjulian#endif 9752419Sjulian 9852722Sjulianstatic ng_ID_t nextID = 1; 9952722Sjulian 10053403Sarchie#ifdef INVARIANTS 10153403Sarchie#define CHECK_DATA_MBUF(m) do { \ 10253403Sarchie struct mbuf *n; \ 10353403Sarchie int total; \ 10453403Sarchie \ 10553403Sarchie if (((m)->m_flags & M_PKTHDR) == 0) \ 10653403Sarchie panic("%s: !PKTHDR", __FUNCTION__); \ 10753403Sarchie for (total = 0, n = (m); n != NULL; n = n->m_next) \ 10853403Sarchie total += n->m_len; \ 10953403Sarchie if ((m)->m_pkthdr.len != total) { \ 11053403Sarchie panic("%s: %d != %d", \ 11153403Sarchie __FUNCTION__, (m)->m_pkthdr.len, total); \ 11253403Sarchie } \ 11353403Sarchie } while (0) 11453403Sarchie#else 11553403Sarchie#define CHECK_DATA_MBUF(m) 11653403Sarchie#endif 11752722Sjulian 11853403Sarchie 11952419Sjulian/************************************************************************ 12053913Sarchie Parse type definitions for generic messages 12153913Sarchie************************************************************************/ 12253913Sarchie 12353913Sarchie/* Handy structure parse type defining macro */ 12453913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ 12553913Sarchiestatic const struct ng_parse_struct_info \ 12653913Sarchie ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args; \ 12753913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = { \ 12853913Sarchie &ng_parse_struct_type, \ 12953913Sarchie &ng_ ## lo ## _type_info \ 13053913Sarchie} 13153913Sarchie 13253913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); 13353913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); 13453913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); 13553913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); 13653913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); 13753913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); 13853913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); 13953913Sarchie 14053913Sarchie/* Get length of an array when the length is stored as a 32 bit 14153913Sarchie value immediately preceeding the array -- as with struct namelist 14253913Sarchie and struct typelist. */ 14353913Sarchiestatic int 14453913Sarchieng_generic_list_getLength(const struct ng_parse_type *type, 14553913Sarchie const u_char *start, const u_char *buf) 14653913Sarchie{ 14753913Sarchie return *((const u_int32_t *)(buf - 4)); 14853913Sarchie} 14953913Sarchie 15053913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */ 15153913Sarchiestatic int 15253913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type, 15353913Sarchie const u_char *start, const u_char *buf) 15453913Sarchie{ 15553913Sarchie const struct hooklist *hl = (const struct hooklist *)start; 15653913Sarchie 15753913Sarchie return hl->nodeinfo.hooks; 15853913Sarchie} 15953913Sarchie 16053913Sarchie/* Array type for a variable length array of struct namelist */ 16153913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = { 16253913Sarchie &ng_generic_nodeinfo_type, 16353913Sarchie &ng_generic_list_getLength 16453913Sarchie}; 16553913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = { 16653913Sarchie &ng_parse_array_type, 16753913Sarchie &ng_nodeinfoarray_type_info 16853913Sarchie}; 16953913Sarchie 17053913Sarchie/* Array type for a variable length array of struct typelist */ 17153913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = { 17253913Sarchie &ng_generic_typeinfo_type, 17353913Sarchie &ng_generic_list_getLength 17453913Sarchie}; 17553913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = { 17653913Sarchie &ng_parse_array_type, 17753913Sarchie &ng_typeinfoarray_type_info 17853913Sarchie}; 17953913Sarchie 18053913Sarchie/* Array type for array of struct linkinfo in struct hooklist */ 18153913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { 18253913Sarchie &ng_generic_linkinfo_type, 18353913Sarchie &ng_generic_linkinfo_getLength 18453913Sarchie}; 18553913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = { 18653913Sarchie &ng_parse_array_type, 18753913Sarchie &ng_generic_linkinfo_array_type_info 18853913Sarchie}; 18953913Sarchie 19053913SarchieDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); 19153913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, 19253913Sarchie (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); 19353913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, 19453913Sarchie (&ng_generic_nodeinfoarray_type)); 19553913Sarchie 19653913Sarchie/* List of commands and how to convert arguments to/from ASCII */ 19753913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = { 19853913Sarchie { 19953913Sarchie NGM_GENERIC_COOKIE, 20053913Sarchie NGM_SHUTDOWN, 20153913Sarchie "shutdown", 20253913Sarchie NULL, 20353913Sarchie NULL 20453913Sarchie }, 20553913Sarchie { 20653913Sarchie NGM_GENERIC_COOKIE, 20753913Sarchie NGM_MKPEER, 20853913Sarchie "mkpeer", 20953913Sarchie &ng_generic_mkpeer_type, 21053913Sarchie NULL 21153913Sarchie }, 21253913Sarchie { 21353913Sarchie NGM_GENERIC_COOKIE, 21453913Sarchie NGM_CONNECT, 21553913Sarchie "connect", 21653913Sarchie &ng_generic_connect_type, 21753913Sarchie NULL 21853913Sarchie }, 21953913Sarchie { 22053913Sarchie NGM_GENERIC_COOKIE, 22153913Sarchie NGM_NAME, 22253913Sarchie "name", 22353913Sarchie &ng_generic_name_type, 22453913Sarchie NULL 22553913Sarchie }, 22653913Sarchie { 22753913Sarchie NGM_GENERIC_COOKIE, 22853913Sarchie NGM_RMHOOK, 22953913Sarchie "rmhook", 23053913Sarchie &ng_generic_rmhook_type, 23153913Sarchie NULL 23253913Sarchie }, 23353913Sarchie { 23453913Sarchie NGM_GENERIC_COOKIE, 23553913Sarchie NGM_NODEINFO, 23653913Sarchie "nodeinfo", 23753913Sarchie NULL, 23853913Sarchie &ng_generic_nodeinfo_type 23953913Sarchie }, 24053913Sarchie { 24153913Sarchie NGM_GENERIC_COOKIE, 24253913Sarchie NGM_LISTHOOKS, 24353913Sarchie "listhooks", 24453913Sarchie NULL, 24553913Sarchie &ng_generic_hooklist_type 24653913Sarchie }, 24753913Sarchie { 24853913Sarchie NGM_GENERIC_COOKIE, 24953913Sarchie NGM_LISTNAMES, 25053913Sarchie "listnames", 25153913Sarchie NULL, 25253913Sarchie &ng_generic_listnodes_type /* same as NGM_LISTNODES */ 25353913Sarchie }, 25453913Sarchie { 25553913Sarchie NGM_GENERIC_COOKIE, 25653913Sarchie NGM_LISTNODES, 25753913Sarchie "listnodes", 25853913Sarchie NULL, 25953913Sarchie &ng_generic_listnodes_type 26053913Sarchie }, 26153913Sarchie { 26253913Sarchie NGM_GENERIC_COOKIE, 26353913Sarchie NGM_LISTTYPES, 26453913Sarchie "listtypes", 26553913Sarchie NULL, 26653913Sarchie &ng_generic_typeinfo_type 26753913Sarchie }, 26853913Sarchie { 26953913Sarchie NGM_GENERIC_COOKIE, 27062471Sphk NGM_TEXT_CONFIG, 27162471Sphk "textconfig", 27262471Sphk NULL, 27362471Sphk &ng_parse_string_type 27462471Sphk }, 27562471Sphk { 27662471Sphk NGM_GENERIC_COOKIE, 27753913Sarchie NGM_TEXT_STATUS, 27853913Sarchie "textstatus", 27953913Sarchie NULL, 28053913Sarchie &ng_parse_string_type 28153913Sarchie }, 28253913Sarchie { 28353913Sarchie NGM_GENERIC_COOKIE, 28453913Sarchie NGM_ASCII2BINARY, 28553913Sarchie "ascii2binary", 28653913Sarchie &ng_parse_ng_mesg_type, 28753913Sarchie &ng_parse_ng_mesg_type 28853913Sarchie }, 28953913Sarchie { 29053913Sarchie NGM_GENERIC_COOKIE, 29153913Sarchie NGM_BINARY2ASCII, 29253913Sarchie "binary2ascii", 29353913Sarchie &ng_parse_ng_mesg_type, 29453913Sarchie &ng_parse_ng_mesg_type 29553913Sarchie }, 29653913Sarchie { 0 } 29753913Sarchie}; 29853913Sarchie 29953913Sarchie/************************************************************************ 30052419Sjulian Node routines 30152419Sjulian************************************************************************/ 30252419Sjulian 30352419Sjulian/* 30452419Sjulian * Instantiate a node of the requested type 30552419Sjulian */ 30652419Sjulianint 30752419Sjulianng_make_node(const char *typename, node_p *nodepp) 30852419Sjulian{ 30952419Sjulian struct ng_type *type; 31052419Sjulian 31152419Sjulian /* Check that the type makes sense */ 31252419Sjulian if (typename == NULL) { 31352419Sjulian TRAP_ERROR; 31452419Sjulian return (EINVAL); 31552419Sjulian } 31652419Sjulian 31752419Sjulian /* Locate the node type */ 31852419Sjulian if ((type = ng_findtype(typename)) == NULL) { 31959875Speter char filename[NG_TYPELEN + 4]; 32052419Sjulian linker_file_t lf; 32152419Sjulian int error; 32252419Sjulian 32352419Sjulian /* Not found, try to load it as a loadable module */ 32459875Speter snprintf(filename, sizeof(filename), "ng_%s", typename); 32559875Speter error = linker_load_file(filename, &lf); 32652419Sjulian if (error != 0) 32752419Sjulian return (error); 32852419Sjulian lf->userrefs++; /* pretend loaded by the syscall */ 32952419Sjulian 33052419Sjulian /* Try again, as now the type should have linked itself in */ 33152419Sjulian if ((type = ng_findtype(typename)) == NULL) 33252419Sjulian return (ENXIO); 33352419Sjulian } 33452419Sjulian 33552419Sjulian /* Call the constructor */ 33652419Sjulian if (type->constructor != NULL) 33752419Sjulian return ((*type->constructor)(nodepp)); 33852419Sjulian else 33952419Sjulian return (ng_make_node_common(type, nodepp)); 34052419Sjulian} 34152419Sjulian 34252419Sjulian/* 34352419Sjulian * Generic node creation. Called by node constructors. 34452419Sjulian * The returned node has a reference count of 1. 34552419Sjulian */ 34652419Sjulianint 34752419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp) 34852419Sjulian{ 34952419Sjulian node_p node; 35052419Sjulian 35152419Sjulian /* Require the node type to have been already installed */ 35252419Sjulian if (ng_findtype(type->name) == NULL) { 35352419Sjulian TRAP_ERROR; 35452419Sjulian return (EINVAL); 35552419Sjulian } 35652419Sjulian 35752419Sjulian /* Make a node and try attach it to the type */ 35863675Sarchie MALLOC(node, node_p, sizeof(*node), M_NETGRAPH, M_NOWAIT); 35952419Sjulian if (node == NULL) { 36052419Sjulian TRAP_ERROR; 36152419Sjulian return (ENOMEM); 36252419Sjulian } 36352419Sjulian bzero(node, sizeof(*node)); 36452419Sjulian node->type = type; 36552419Sjulian node->refs++; /* note reference */ 36652419Sjulian type->refs++; 36752419Sjulian 36852419Sjulian /* Link us into the node linked list */ 36952419Sjulian LIST_INSERT_HEAD(&nodelist, node, nodes); 37052419Sjulian 37152419Sjulian /* Initialize hook list for new node */ 37252419Sjulian LIST_INIT(&node->hooks); 37352419Sjulian 37452722Sjulian /* get an ID and put us in the hash chain */ 37552722Sjulian node->ID = nextID++; /* 137 per second for 1 year before wrap */ 37652722Sjulian LIST_INSERT_HEAD(&ID_hash[node->ID % ID_HASH_SIZE], node, idnodes); 37752722Sjulian 37852419Sjulian /* Done */ 37952419Sjulian *nodepp = node; 38052419Sjulian return (0); 38152419Sjulian} 38252419Sjulian 38352419Sjulian/* 38452419Sjulian * Forceably start the shutdown process on a node. Either call 38552419Sjulian * it's shutdown method, or do the default shutdown if there is 38652419Sjulian * no type-specific method. 38752419Sjulian * 38852419Sjulian * Persistent nodes must have a type-specific method which 38952419Sjulian * resets the NG_INVALID flag. 39052419Sjulian */ 39152419Sjulianvoid 39252419Sjulianng_rmnode(node_p node) 39352419Sjulian{ 39452419Sjulian /* Check if it's already shutting down */ 39552419Sjulian if ((node->flags & NG_INVALID) != 0) 39652419Sjulian return; 39752419Sjulian 39852419Sjulian /* Add an extra reference so it doesn't go away during this */ 39952419Sjulian node->refs++; 40052419Sjulian 40152419Sjulian /* Mark it invalid so any newcomers know not to try use it */ 40252419Sjulian node->flags |= NG_INVALID; 40352419Sjulian 40452419Sjulian /* Ask the type if it has anything to do in this case */ 40552419Sjulian if (node->type && node->type->shutdown) 40652419Sjulian (*node->type->shutdown)(node); 40752419Sjulian else { /* do the default thing */ 40852419Sjulian ng_unname(node); 40952419Sjulian ng_cutlinks(node); 41052419Sjulian ng_unref(node); 41152419Sjulian } 41252419Sjulian 41352419Sjulian /* Remove extra reference, possibly the last */ 41452419Sjulian ng_unref(node); 41552419Sjulian} 41652419Sjulian 41752419Sjulian/* 41852419Sjulian * Called by the destructor to remove any STANDARD external references 41952419Sjulian */ 42052419Sjulianvoid 42152419Sjulianng_cutlinks(node_p node) 42252419Sjulian{ 42352419Sjulian hook_p hook; 42452419Sjulian 42552419Sjulian /* Make sure that this is set to stop infinite loops */ 42652419Sjulian node->flags |= NG_INVALID; 42752419Sjulian 42852419Sjulian /* If we have sleepers, wake them up; they'll see NG_INVALID */ 42952419Sjulian if (node->sleepers) 43052419Sjulian wakeup(node); 43152419Sjulian 43252419Sjulian /* Notify all remaining connected nodes to disconnect */ 43352419Sjulian while ((hook = LIST_FIRST(&node->hooks)) != NULL) 43452419Sjulian ng_destroy_hook(hook); 43552419Sjulian} 43652419Sjulian 43752419Sjulian/* 43852419Sjulian * Remove a reference to the node, possibly the last 43952419Sjulian */ 44052419Sjulianvoid 44152419Sjulianng_unref(node_p node) 44252419Sjulian{ 44352419Sjulian if (--node->refs <= 0) { 44452419Sjulian node->type->refs--; 44552419Sjulian LIST_REMOVE(node, nodes); 44652722Sjulian LIST_REMOVE(node, idnodes); 44752419Sjulian FREE(node, M_NETGRAPH); 44852419Sjulian } 44952419Sjulian} 45052419Sjulian 45152419Sjulian/* 45252419Sjulian * Wait for a node to come ready. Returns a node with a reference count; 45352419Sjulian * don't forget to drop it when we are done with it using ng_release_node(). 45452419Sjulian */ 45552419Sjulianint 45652419Sjulianng_wait_node(node_p node, char *msg) 45752419Sjulian{ 45852419Sjulian int s, error = 0; 45952419Sjulian 46052419Sjulian if (msg == NULL) 46152419Sjulian msg = "netgraph"; 46252419Sjulian s = splnet(); 46352419Sjulian node->sleepers++; 46452419Sjulian node->refs++; /* the sleeping process counts as a reference */ 46552419Sjulian while ((node->flags & (NG_BUSY | NG_INVALID)) == NG_BUSY) 46652419Sjulian error = tsleep(node, (PZERO + 1) | PCATCH, msg, 0); 46752419Sjulian node->sleepers--; 46852419Sjulian if (node->flags & NG_INVALID) { 46952419Sjulian TRAP_ERROR; 47052419Sjulian error = ENXIO; 47152419Sjulian } else { 47253403Sarchie KASSERT(node->refs > 1, 47353403Sarchie ("%s: refs=%d", __FUNCTION__, node->refs)); 47452419Sjulian node->flags |= NG_BUSY; 47552419Sjulian } 47652419Sjulian splx(s); 47752419Sjulian 47852419Sjulian /* Release the reference we had on it */ 47952419Sjulian if (error != 0) 48052419Sjulian ng_unref(node); 48152419Sjulian return error; 48252419Sjulian} 48352419Sjulian 48452419Sjulian/* 48552419Sjulian * Release a node acquired via ng_wait_node() 48652419Sjulian */ 48752419Sjulianvoid 48852419Sjulianng_release_node(node_p node) 48952419Sjulian{ 49052419Sjulian /* Declare that we don't want it */ 49152419Sjulian node->flags &= ~NG_BUSY; 49252419Sjulian 49352419Sjulian /* If we have sleepers, then wake them up */ 49452419Sjulian if (node->sleepers) 49552419Sjulian wakeup(node); 49652419Sjulian 49752419Sjulian /* We also have a reference.. drop it too */ 49852419Sjulian ng_unref(node); 49952419Sjulian} 50052419Sjulian 50152419Sjulian/************************************************************************ 50252722Sjulian Node ID handling 50352722Sjulian************************************************************************/ 50452722Sjulianstatic node_p 50552722Sjulianng_ID2node(ng_ID_t ID) 50652722Sjulian{ 50752722Sjulian node_p np; 50852722Sjulian LIST_FOREACH(np, &ID_hash[ID % ID_HASH_SIZE], idnodes) { 50952722Sjulian if (np->ID == ID) 51052722Sjulian break; 51152722Sjulian } 51252722Sjulian return(np); 51352722Sjulian} 51452722Sjulian 51552722Sjulianng_ID_t 51652722Sjulianng_node2ID(node_p node) 51752722Sjulian{ 51852722Sjulian return (node->ID); 51952722Sjulian} 52052722Sjulian 52152722Sjulian/************************************************************************ 52252419Sjulian Node name handling 52352419Sjulian************************************************************************/ 52452419Sjulian 52552419Sjulian/* 52652419Sjulian * Assign a node a name. Once assigned, the name cannot be changed. 52752419Sjulian */ 52852419Sjulianint 52952419Sjulianng_name_node(node_p node, const char *name) 53052419Sjulian{ 53152419Sjulian int i; 53252419Sjulian 53352419Sjulian /* Check the name is valid */ 53452419Sjulian for (i = 0; i < NG_NODELEN + 1; i++) { 53552419Sjulian if (name[i] == '\0' || name[i] == '.' || name[i] == ':') 53652419Sjulian break; 53752419Sjulian } 53852419Sjulian if (i == 0 || name[i] != '\0') { 53952419Sjulian TRAP_ERROR; 54052419Sjulian return (EINVAL); 54152419Sjulian } 54252722Sjulian if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ 54352419Sjulian TRAP_ERROR; 54452419Sjulian return (EINVAL); 54552419Sjulian } 54652419Sjulian 54752419Sjulian /* Check the node isn't already named */ 54852419Sjulian if (node->name != NULL) { 54952419Sjulian TRAP_ERROR; 55052419Sjulian return (EISCONN); 55152419Sjulian } 55252419Sjulian 55352419Sjulian /* Check the name isn't already being used */ 55452419Sjulian if (ng_findname(node, name) != NULL) { 55552419Sjulian TRAP_ERROR; 55652419Sjulian return (EADDRINUSE); 55752419Sjulian } 55852419Sjulian 55952419Sjulian /* Allocate space and copy it */ 56063675Sarchie MALLOC(node->name, char *, strlen(name) + 1, M_NETGRAPH, M_NOWAIT); 56152419Sjulian if (node->name == NULL) { 56252419Sjulian TRAP_ERROR; 56352419Sjulian return (ENOMEM); 56452419Sjulian } 56552419Sjulian strcpy(node->name, name); 56652419Sjulian 56752419Sjulian /* The name counts as a reference */ 56852419Sjulian node->refs++; 56952419Sjulian return (0); 57052419Sjulian} 57152419Sjulian 57252419Sjulian/* 57352419Sjulian * Find a node by absolute name. The name should NOT end with ':' 57452419Sjulian * The name "." means "this node" and "[xxx]" means "the node 57552419Sjulian * with ID (ie, at address) xxx". 57652419Sjulian * 57752419Sjulian * Returns the node if found, else NULL. 57852419Sjulian */ 57952419Sjuliannode_p 58052419Sjulianng_findname(node_p this, const char *name) 58152419Sjulian{ 58252722Sjulian node_p node; 58352722Sjulian ng_ID_t temp; 58452419Sjulian 58552419Sjulian /* "." means "this node" */ 58652419Sjulian if (strcmp(name, ".") == 0) 58752419Sjulian return(this); 58852419Sjulian 58952419Sjulian /* Check for name-by-ID */ 59052722Sjulian if ((temp = ng_decodeidname(name)) != 0) { 59152722Sjulian return (ng_ID2node(temp)); 59252419Sjulian } 59352419Sjulian 59452419Sjulian /* Find node by name */ 59552419Sjulian LIST_FOREACH(node, &nodelist, nodes) { 59652419Sjulian if (node->name != NULL && strcmp(node->name, name) == 0) 59752419Sjulian break; 59852419Sjulian } 59952419Sjulian return (node); 60052419Sjulian} 60152419Sjulian 60252419Sjulian/* 60352722Sjulian * Decode a ID name, eg. "[f03034de]". Returns 0 if the 60452722Sjulian * string is not valid, otherwise returns the value. 60552419Sjulian */ 60652722Sjulianstatic ng_ID_t 60752419Sjulianng_decodeidname(const char *name) 60852419Sjulian{ 60952816Sarchie const int len = strlen(name); 61053648Sarchie char *eptr; 61152816Sarchie u_long val; 61252419Sjulian 61352816Sarchie /* Check for proper length, brackets, no leading junk */ 61452816Sarchie if (len < 3 || name[0] != '[' || name[len - 1] != ']' 61552951Sjulian || !isxdigit(name[1])) 61652816Sarchie return (0); 61752419Sjulian 61852816Sarchie /* Decode number */ 61952816Sarchie val = strtoul(name + 1, &eptr, 16); 62052951Sjulian if (eptr - name != len - 1 || val == ULONG_MAX || val == 0) 62153042Sjulian return ((ng_ID_t)0); 62252816Sarchie return (ng_ID_t)val; 62352419Sjulian} 62452419Sjulian 62552419Sjulian/* 62652419Sjulian * Remove a name from a node. This should only be called 62752419Sjulian * when shutting down and removing the node. 62852419Sjulian */ 62952419Sjulianvoid 63052419Sjulianng_unname(node_p node) 63152419Sjulian{ 63252419Sjulian if (node->name) { 63352419Sjulian FREE(node->name, M_NETGRAPH); 63452419Sjulian node->name = NULL; 63552419Sjulian ng_unref(node); 63652419Sjulian } 63752419Sjulian} 63852419Sjulian 63952419Sjulian/************************************************************************ 64052419Sjulian Hook routines 64152419Sjulian 64252419Sjulian Names are not optional. Hooks are always connected, except for a 64352419Sjulian brief moment within these routines. 64452419Sjulian 64552419Sjulian************************************************************************/ 64652419Sjulian 64752419Sjulian/* 64852419Sjulian * Remove a hook reference 64952419Sjulian */ 65052419Sjulianstatic void 65152419Sjulianng_unref_hook(hook_p hook) 65252419Sjulian{ 65352419Sjulian if (--hook->refs == 0) 65452419Sjulian FREE(hook, M_NETGRAPH); 65552419Sjulian} 65652419Sjulian 65752419Sjulian/* 65852419Sjulian * Add an unconnected hook to a node. Only used internally. 65952419Sjulian */ 66052419Sjulianstatic int 66152419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp) 66252419Sjulian{ 66352419Sjulian hook_p hook; 66452419Sjulian int error = 0; 66552419Sjulian 66652419Sjulian /* Check that the given name is good */ 66752419Sjulian if (name == NULL) { 66852419Sjulian TRAP_ERROR; 66952419Sjulian return (EINVAL); 67052419Sjulian } 67154096Sarchie if (ng_findhook(node, name) != NULL) { 67254096Sarchie TRAP_ERROR; 67354096Sarchie return (EEXIST); 67452419Sjulian } 67552419Sjulian 67652419Sjulian /* Allocate the hook and link it up */ 67763675Sarchie MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH, M_NOWAIT); 67852419Sjulian if (hook == NULL) { 67952419Sjulian TRAP_ERROR; 68052419Sjulian return (ENOMEM); 68152419Sjulian } 68252419Sjulian bzero(hook, sizeof(*hook)); 68352419Sjulian hook->refs = 1; 68452419Sjulian hook->flags = HK_INVALID; 68552419Sjulian hook->node = node; 68652419Sjulian node->refs++; /* each hook counts as a reference */ 68752419Sjulian 68852419Sjulian /* Check if the node type code has something to say about it */ 68952419Sjulian if (node->type->newhook != NULL) 69052419Sjulian if ((error = (*node->type->newhook)(node, hook, name)) != 0) 69152419Sjulian goto fail; 69252419Sjulian 69352419Sjulian /* 69452419Sjulian * The 'type' agrees so far, so go ahead and link it in. 69552419Sjulian * We'll ask again later when we actually connect the hooks. 69652419Sjulian */ 69752419Sjulian LIST_INSERT_HEAD(&node->hooks, hook, hooks); 69852419Sjulian node->numhooks++; 69952419Sjulian 70052419Sjulian /* Set hook name */ 70163675Sarchie MALLOC(hook->name, char *, strlen(name) + 1, M_NETGRAPH, M_NOWAIT); 70252419Sjulian if (hook->name == NULL) { 70352419Sjulian error = ENOMEM; 70452419Sjulian LIST_REMOVE(hook, hooks); 70552419Sjulian node->numhooks--; 70652419Sjulianfail: 70752419Sjulian hook->node = NULL; 70852419Sjulian ng_unref(node); 70952419Sjulian ng_unref_hook(hook); /* this frees the hook */ 71052419Sjulian return (error); 71152419Sjulian } 71252419Sjulian strcpy(hook->name, name); 71352419Sjulian if (hookp) 71452419Sjulian *hookp = hook; 71552419Sjulian return (error); 71652419Sjulian} 71752419Sjulian 71852419Sjulian/* 71952419Sjulian * Connect a pair of hooks. Only used internally. 72052419Sjulian */ 72152419Sjulianstatic int 72252419Sjulianng_connect(hook_p hook1, hook_p hook2) 72352419Sjulian{ 72452419Sjulian int error; 72552419Sjulian 72652419Sjulian hook1->peer = hook2; 72752419Sjulian hook2->peer = hook1; 72852419Sjulian 72952419Sjulian /* Give each node the opportunity to veto the impending connection */ 73052419Sjulian if (hook1->node->type->connect) { 73152419Sjulian if ((error = (*hook1->node->type->connect) (hook1))) { 73252419Sjulian ng_destroy_hook(hook1); /* also zaps hook2 */ 73352419Sjulian return (error); 73452419Sjulian } 73552419Sjulian } 73652419Sjulian if (hook2->node->type->connect) { 73752419Sjulian if ((error = (*hook2->node->type->connect) (hook2))) { 73852419Sjulian ng_destroy_hook(hook2); /* also zaps hook1 */ 73952419Sjulian return (error); 74052419Sjulian } 74152419Sjulian } 74252419Sjulian hook1->flags &= ~HK_INVALID; 74352419Sjulian hook2->flags &= ~HK_INVALID; 74452419Sjulian return (0); 74552419Sjulian} 74652419Sjulian 74752419Sjulian/* 74854096Sarchie * Find a hook 74954096Sarchie * 75054096Sarchie * Node types may supply their own optimized routines for finding 75154096Sarchie * hooks. If none is supplied, we just do a linear search. 75254096Sarchie */ 75354096Sarchiehook_p 75454096Sarchieng_findhook(node_p node, const char *name) 75554096Sarchie{ 75654096Sarchie hook_p hook; 75754096Sarchie 75854096Sarchie if (node->type->findhook != NULL) 75954096Sarchie return (*node->type->findhook)(node, name); 76054096Sarchie LIST_FOREACH(hook, &node->hooks, hooks) { 76154096Sarchie if (hook->name != NULL && strcmp(hook->name, name) == 0) 76254096Sarchie return (hook); 76354096Sarchie } 76454096Sarchie return (NULL); 76554096Sarchie} 76654096Sarchie 76754096Sarchie/* 76852419Sjulian * Destroy a hook 76952419Sjulian * 77052419Sjulian * As hooks are always attached, this really destroys two hooks. 77152419Sjulian * The one given, and the one attached to it. Disconnect the hooks 77252419Sjulian * from each other first. 77352419Sjulian */ 77452419Sjulianvoid 77552419Sjulianng_destroy_hook(hook_p hook) 77652419Sjulian{ 77752419Sjulian hook_p peer = hook->peer; 77852419Sjulian 77952419Sjulian hook->flags |= HK_INVALID; /* as soon as possible */ 78052419Sjulian if (peer) { 78152419Sjulian peer->flags |= HK_INVALID; /* as soon as possible */ 78252419Sjulian hook->peer = NULL; 78352419Sjulian peer->peer = NULL; 78452419Sjulian ng_disconnect_hook(peer); 78552419Sjulian } 78652419Sjulian ng_disconnect_hook(hook); 78752419Sjulian} 78852419Sjulian 78952419Sjulian/* 79052419Sjulian * Notify the node of the hook's demise. This may result in more actions 79152419Sjulian * (e.g. shutdown) but we don't do that ourselves and don't know what 79252419Sjulian * happens there. If there is no appropriate handler, then just remove it 79352419Sjulian * (and decrement the reference count of it's node which in turn might 79452419Sjulian * make something happen). 79552419Sjulian */ 79652419Sjulianstatic void 79752419Sjulianng_disconnect_hook(hook_p hook) 79852419Sjulian{ 79952419Sjulian node_p node = hook->node; 80052419Sjulian 80152419Sjulian /* 80252419Sjulian * Remove the hook from the node's list to avoid possible recursion 80352419Sjulian * in case the disconnection results in node shutdown. 80452419Sjulian */ 80552419Sjulian LIST_REMOVE(hook, hooks); 80652419Sjulian node->numhooks--; 80752419Sjulian if (node->type->disconnect) { 80852419Sjulian /* 80952419Sjulian * The type handler may elect to destroy the peer so don't 81052419Sjulian * trust its existance after this point. 81152419Sjulian */ 81252419Sjulian (*node->type->disconnect) (hook); 81352419Sjulian } 81452419Sjulian ng_unref(node); /* might be the last reference */ 81552419Sjulian if (hook->name) 81652419Sjulian FREE(hook->name, M_NETGRAPH); 81752419Sjulian hook->node = NULL; /* may still be referenced elsewhere */ 81852419Sjulian ng_unref_hook(hook); 81952419Sjulian} 82052419Sjulian 82152419Sjulian/* 82252419Sjulian * Take two hooks on a node and merge the connection so that the given node 82352419Sjulian * is effectively bypassed. 82452419Sjulian */ 82552419Sjulianint 82652419Sjulianng_bypass(hook_p hook1, hook_p hook2) 82752419Sjulian{ 82852419Sjulian if (hook1->node != hook2->node) 82952419Sjulian return (EINVAL); 83052419Sjulian hook1->peer->peer = hook2->peer; 83152419Sjulian hook2->peer->peer = hook1->peer; 83252419Sjulian 83352419Sjulian /* XXX If we ever cache methods on hooks update them as well */ 83452419Sjulian hook1->peer = NULL; 83552419Sjulian hook2->peer = NULL; 83652419Sjulian ng_destroy_hook(hook1); 83752419Sjulian ng_destroy_hook(hook2); 83852419Sjulian return (0); 83952419Sjulian} 84052419Sjulian 84152419Sjulian/* 84252419Sjulian * Install a new netgraph type 84352419Sjulian */ 84452419Sjulianint 84552419Sjulianng_newtype(struct ng_type *tp) 84652419Sjulian{ 84752419Sjulian const size_t namelen = strlen(tp->name); 84852419Sjulian 84952419Sjulian /* Check version and type name fields */ 85052419Sjulian if (tp->version != NG_VERSION || namelen == 0 || namelen > NG_TYPELEN) { 85152419Sjulian TRAP_ERROR; 85252419Sjulian return (EINVAL); 85352419Sjulian } 85452419Sjulian 85552419Sjulian /* Check for name collision */ 85652419Sjulian if (ng_findtype(tp->name) != NULL) { 85752419Sjulian TRAP_ERROR; 85852419Sjulian return (EEXIST); 85952419Sjulian } 86052419Sjulian 86152419Sjulian /* Link in new type */ 86252419Sjulian LIST_INSERT_HEAD(&typelist, tp, types); 86352419Sjulian tp->refs = 0; 86452419Sjulian return (0); 86552419Sjulian} 86652419Sjulian 86752419Sjulian/* 86852419Sjulian * Look for a type of the name given 86952419Sjulian */ 87052419Sjulianstruct ng_type * 87152419Sjulianng_findtype(const char *typename) 87252419Sjulian{ 87352419Sjulian struct ng_type *type; 87452419Sjulian 87552419Sjulian LIST_FOREACH(type, &typelist, types) { 87652419Sjulian if (strcmp(type->name, typename) == 0) 87752419Sjulian break; 87852419Sjulian } 87952419Sjulian return (type); 88052419Sjulian} 88152419Sjulian 88252419Sjulian 88352419Sjulian/************************************************************************ 88452419Sjulian Composite routines 88552419Sjulian************************************************************************/ 88652419Sjulian 88752419Sjulian/* 88852419Sjulian * Make a peer and connect. The order is arranged to minimise 88952419Sjulian * the work needed to back out in case of error. 89052419Sjulian */ 89152419Sjulianint 89252419Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type) 89352419Sjulian{ 89452419Sjulian node_p node2; 89552419Sjulian hook_p hook; 89652419Sjulian hook_p hook2; 89752419Sjulian int error; 89852419Sjulian 89952419Sjulian if ((error = ng_add_hook(node, name, &hook))) 90052419Sjulian return (error); 90152419Sjulian if ((error = ng_make_node(type, &node2))) { 90252419Sjulian ng_destroy_hook(hook); 90352419Sjulian return (error); 90452419Sjulian } 90552419Sjulian if ((error = ng_add_hook(node2, name2, &hook2))) { 90652419Sjulian ng_rmnode(node2); 90752419Sjulian ng_destroy_hook(hook); 90852419Sjulian return (error); 90952419Sjulian } 91052419Sjulian 91152419Sjulian /* 91252419Sjulian * Actually link the two hooks together.. on failure they are 91352419Sjulian * destroyed so we don't have to do that here. 91452419Sjulian */ 91552419Sjulian if ((error = ng_connect(hook, hook2))) 91652419Sjulian ng_rmnode(node2); 91752419Sjulian return (error); 91852419Sjulian} 91952419Sjulian 92052419Sjulian/* 92152419Sjulian * Connect two nodes using the specified hooks 92252419Sjulian */ 92352419Sjulianint 92452419Sjulianng_con_nodes(node_p node, const char *name, node_p node2, const char *name2) 92552419Sjulian{ 92652419Sjulian int error; 92752419Sjulian hook_p hook; 92852419Sjulian hook_p hook2; 92952419Sjulian 93052419Sjulian if ((error = ng_add_hook(node, name, &hook))) 93152419Sjulian return (error); 93252419Sjulian if ((error = ng_add_hook(node2, name2, &hook2))) { 93352419Sjulian ng_destroy_hook(hook); 93452419Sjulian return (error); 93552419Sjulian } 93652419Sjulian return (ng_connect(hook, hook2)); 93752419Sjulian} 93852419Sjulian 93952419Sjulian/* 94052419Sjulian * Parse and verify a string of the form: <NODE:><PATH> 94152419Sjulian * 94252419Sjulian * Such a string can refer to a specific node or a specific hook 94352419Sjulian * on a specific node, depending on how you look at it. In the 94452419Sjulian * latter case, the PATH component must not end in a dot. 94552419Sjulian * 94652419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string 94752419Sjulian * of hook names separated by dots. This breaks out the original 94852419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp 94952419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to 95052419Sjulian * the final hook component of <PATH>, if any, otherwise NULL. 95152419Sjulian * 95252419Sjulian * This returns -1 if the path is malformed. The char ** are optional. 95352419Sjulian */ 95452419Sjulian 95552419Sjulianint 95652419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 95752419Sjulian{ 95852419Sjulian char *node, *path, *hook; 95952419Sjulian int k; 96052419Sjulian 96152419Sjulian /* 96252419Sjulian * Extract absolute NODE, if any 96352419Sjulian */ 96452419Sjulian for (path = addr; *path && *path != ':'; path++); 96552419Sjulian if (*path) { 96652419Sjulian node = addr; /* Here's the NODE */ 96752419Sjulian *path++ = '\0'; /* Here's the PATH */ 96852419Sjulian 96952419Sjulian /* Node name must not be empty */ 97052419Sjulian if (!*node) 97152419Sjulian return -1; 97252419Sjulian 97352419Sjulian /* A name of "." is OK; otherwise '.' not allowed */ 97452419Sjulian if (strcmp(node, ".") != 0) { 97552419Sjulian for (k = 0; node[k]; k++) 97652419Sjulian if (node[k] == '.') 97752419Sjulian return -1; 97852419Sjulian } 97952419Sjulian } else { 98052419Sjulian node = NULL; /* No absolute NODE */ 98152419Sjulian path = addr; /* Here's the PATH */ 98252419Sjulian } 98352419Sjulian 98452419Sjulian /* Snoop for illegal characters in PATH */ 98552419Sjulian for (k = 0; path[k]; k++) 98652419Sjulian if (path[k] == ':') 98752419Sjulian return -1; 98852419Sjulian 98952419Sjulian /* Check for no repeated dots in PATH */ 99052419Sjulian for (k = 0; path[k]; k++) 99152419Sjulian if (path[k] == '.' && path[k + 1] == '.') 99252419Sjulian return -1; 99352419Sjulian 99452419Sjulian /* Remove extra (degenerate) dots from beginning or end of PATH */ 99552419Sjulian if (path[0] == '.') 99652419Sjulian path++; 99752419Sjulian if (*path && path[strlen(path) - 1] == '.') 99852419Sjulian path[strlen(path) - 1] = 0; 99952419Sjulian 100052419Sjulian /* If PATH has a dot, then we're not talking about a hook */ 100152419Sjulian if (*path) { 100252419Sjulian for (hook = path, k = 0; path[k]; k++) 100352419Sjulian if (path[k] == '.') { 100452419Sjulian hook = NULL; 100552419Sjulian break; 100652419Sjulian } 100752419Sjulian } else 100852419Sjulian path = hook = NULL; 100952419Sjulian 101052419Sjulian /* Done */ 101152419Sjulian if (nodep) 101252419Sjulian *nodep = node; 101352419Sjulian if (pathp) 101452419Sjulian *pathp = path; 101552419Sjulian if (hookp) 101652419Sjulian *hookp = hook; 101752419Sjulian return (0); 101852419Sjulian} 101952419Sjulian 102052419Sjulian/* 102152419Sjulian * Given a path, which may be absolute or relative, and a starting node, 102252419Sjulian * return the destination node. Compute the "return address" if desired. 102352419Sjulian */ 102452419Sjulianint 102559728Sjulianng_path2node(node_p here, const char *address, node_p *destp, char **rtnp, 102659728Sjulian hook_p *lasthook) 102752419Sjulian{ 102852419Sjulian const node_p start = here; 102952419Sjulian char fullpath[NG_PATHLEN + 1]; 103052419Sjulian char *nodename, *path, pbuf[2]; 103152419Sjulian node_p node; 103252419Sjulian char *cp; 103359728Sjulian hook_p hook = NULL; 103452419Sjulian 103552419Sjulian /* Initialize */ 103652419Sjulian if (rtnp) 103752419Sjulian *rtnp = NULL; 103852419Sjulian if (destp == NULL) 103952419Sjulian return EINVAL; 104052419Sjulian *destp = NULL; 104152419Sjulian 104252419Sjulian /* Make a writable copy of address for ng_path_parse() */ 104352419Sjulian strncpy(fullpath, address, sizeof(fullpath) - 1); 104452419Sjulian fullpath[sizeof(fullpath) - 1] = '\0'; 104552419Sjulian 104652419Sjulian /* Parse out node and sequence of hooks */ 104752419Sjulian if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 104852419Sjulian TRAP_ERROR; 104952419Sjulian return EINVAL; 105052419Sjulian } 105152419Sjulian if (path == NULL) { 105252419Sjulian pbuf[0] = '.'; /* Needs to be writable */ 105352419Sjulian pbuf[1] = '\0'; 105452419Sjulian path = pbuf; 105552419Sjulian } 105652419Sjulian 105752419Sjulian /* For an absolute address, jump to the starting node */ 105852419Sjulian if (nodename) { 105952419Sjulian node = ng_findname(here, nodename); 106052419Sjulian if (node == NULL) { 106152419Sjulian TRAP_ERROR; 106252419Sjulian return (ENOENT); 106352419Sjulian } 106452419Sjulian } else 106552419Sjulian node = here; 106652419Sjulian 106752419Sjulian /* Now follow the sequence of hooks */ 106852419Sjulian for (cp = path; node != NULL && *cp != '\0'; ) { 106952419Sjulian char *segment; 107052419Sjulian 107152419Sjulian /* 107252419Sjulian * Break out the next path segment. Replace the dot we just 107352419Sjulian * found with a NUL; "cp" points to the next segment (or the 107452419Sjulian * NUL at the end). 107552419Sjulian */ 107652419Sjulian for (segment = cp; *cp != '\0'; cp++) { 107752419Sjulian if (*cp == '.') { 107852419Sjulian *cp++ = '\0'; 107952419Sjulian break; 108052419Sjulian } 108152419Sjulian } 108252419Sjulian 108352419Sjulian /* Empty segment */ 108452419Sjulian if (*segment == '\0') 108552419Sjulian continue; 108652419Sjulian 108752419Sjulian /* We have a segment, so look for a hook by that name */ 108854096Sarchie hook = ng_findhook(node, segment); 108952419Sjulian 109052419Sjulian /* Can't get there from here... */ 109152419Sjulian if (hook == NULL 109252419Sjulian || hook->peer == NULL 109352419Sjulian || (hook->flags & HK_INVALID) != 0) { 109452419Sjulian TRAP_ERROR; 109552419Sjulian return (ENOENT); 109652419Sjulian } 109752419Sjulian 109852419Sjulian /* Hop on over to the next node */ 109952419Sjulian node = hook->peer->node; 110052419Sjulian } 110152419Sjulian 110252419Sjulian /* If node somehow missing, fail here (probably this is not needed) */ 110352419Sjulian if (node == NULL) { 110452419Sjulian TRAP_ERROR; 110552419Sjulian return (ENXIO); 110652419Sjulian } 110752419Sjulian 110852419Sjulian /* Now compute return address, i.e., the path to the sender */ 110952419Sjulian if (rtnp != NULL) { 111063675Sarchie MALLOC(*rtnp, char *, NG_NODELEN + 2, M_NETGRAPH, M_NOWAIT); 111152419Sjulian if (*rtnp == NULL) { 111252419Sjulian TRAP_ERROR; 111352419Sjulian return (ENOMEM); 111452419Sjulian } 111552419Sjulian if (start->name != NULL) 111652419Sjulian sprintf(*rtnp, "%s:", start->name); 111752419Sjulian else 111852722Sjulian sprintf(*rtnp, "[%x]:", ng_node2ID(start)); 111952419Sjulian } 112052419Sjulian 112152419Sjulian /* Done */ 112252419Sjulian *destp = node; 112359900Sarchie if (lasthook != NULL) 112459900Sarchie *lasthook = hook ? hook->peer : NULL; 112552419Sjulian return (0); 112652419Sjulian} 112752419Sjulian 112852419Sjulian/* 112952419Sjulian * Call the appropriate message handler for the object. 113052419Sjulian * It is up to the message handler to free the message. 113152419Sjulian * If it's a generic message, handle it generically, otherwise 113252419Sjulian * call the type's message handler (if it exists) 113352419Sjulian */ 113452419Sjulian 113559728Sjulian#define CALL_MSG_HANDLER(error, node, msg, retaddr, resp, hook) \ 113652419Sjuliando { \ 113752419Sjulian if((msg)->header.typecookie == NGM_GENERIC_COOKIE) { \ 113852419Sjulian (error) = ng_generic_msg((node), (msg), \ 113959728Sjulian (retaddr), (resp), (hook)); \ 114052419Sjulian } else { \ 114152419Sjulian if ((node)->type->rcvmsg != NULL) { \ 114252419Sjulian (error) = (*(node)->type->rcvmsg)((node), \ 114359728Sjulian (msg), (retaddr), (resp), (hook)); \ 114452419Sjulian } else { \ 114552419Sjulian TRAP_ERROR; \ 114652419Sjulian FREE((msg), M_NETGRAPH); \ 114752419Sjulian (error) = EINVAL; \ 114852419Sjulian } \ 114952419Sjulian } \ 115052419Sjulian} while (0) 115152419Sjulian 115252419Sjulian 115352419Sjulian/* 115452419Sjulian * Send a control message to a node 115552419Sjulian */ 115652419Sjulianint 115752419Sjulianng_send_msg(node_p here, struct ng_mesg *msg, const char *address, 115852419Sjulian struct ng_mesg **rptr) 115952419Sjulian{ 116052419Sjulian node_p dest = NULL; 116152419Sjulian char *retaddr = NULL; 116252419Sjulian int error; 116359728Sjulian hook_p lasthook; 116452419Sjulian 116552419Sjulian /* Find the target node */ 116659728Sjulian error = ng_path2node(here, address, &dest, &retaddr, &lasthook); 116752419Sjulian if (error) { 116852419Sjulian FREE(msg, M_NETGRAPH); 116952419Sjulian return error; 117052419Sjulian } 117152419Sjulian 117252419Sjulian /* Make sure the resp field is null before we start */ 117352419Sjulian if (rptr != NULL) 117452419Sjulian *rptr = NULL; 117552419Sjulian 117659728Sjulian CALL_MSG_HANDLER(error, dest, msg, retaddr, rptr, lasthook); 117752419Sjulian 117852419Sjulian /* Make sure that if there is a response, it has the RESP bit set */ 117952419Sjulian if ((error == 0) && rptr && *rptr) 118052419Sjulian (*rptr)->header.flags |= NGF_RESP; 118152419Sjulian 118252419Sjulian /* 118352419Sjulian * If we had a return address it is up to us to free it. They should 118452419Sjulian * have taken a copy if they needed to make a delayed response. 118552419Sjulian */ 118652419Sjulian if (retaddr) 118752419Sjulian FREE(retaddr, M_NETGRAPH); 118852419Sjulian return (error); 118952419Sjulian} 119052419Sjulian 119152419Sjulian/* 119252419Sjulian * Implement the 'generic' control messages 119352419Sjulian */ 119452419Sjulianstatic int 119552419Sjulianng_generic_msg(node_p here, struct ng_mesg *msg, const char *retaddr, 119659728Sjulian struct ng_mesg **resp, hook_p lasthook) 119752419Sjulian{ 119852419Sjulian int error = 0; 119952419Sjulian 120052419Sjulian if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 120152419Sjulian TRAP_ERROR; 120252419Sjulian FREE(msg, M_NETGRAPH); 120352419Sjulian return (EINVAL); 120452419Sjulian } 120552419Sjulian switch (msg->header.cmd) { 120652419Sjulian case NGM_SHUTDOWN: 120752419Sjulian ng_rmnode(here); 120852419Sjulian break; 120952419Sjulian case NGM_MKPEER: 121052419Sjulian { 121152419Sjulian struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 121252419Sjulian 121352419Sjulian if (msg->header.arglen != sizeof(*mkp)) { 121452419Sjulian TRAP_ERROR; 121552419Sjulian return (EINVAL); 121652419Sjulian } 121752419Sjulian mkp->type[sizeof(mkp->type) - 1] = '\0'; 121852419Sjulian mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 121952419Sjulian mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 122052419Sjulian error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 122152419Sjulian break; 122252419Sjulian } 122352419Sjulian case NGM_CONNECT: 122452419Sjulian { 122552419Sjulian struct ngm_connect *const con = 122652419Sjulian (struct ngm_connect *) msg->data; 122752419Sjulian node_p node2; 122852419Sjulian 122952419Sjulian if (msg->header.arglen != sizeof(*con)) { 123052419Sjulian TRAP_ERROR; 123152419Sjulian return (EINVAL); 123252419Sjulian } 123352419Sjulian con->path[sizeof(con->path) - 1] = '\0'; 123452419Sjulian con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 123552419Sjulian con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 123659728Sjulian error = ng_path2node(here, con->path, &node2, NULL, NULL); 123752419Sjulian if (error) 123852419Sjulian break; 123952419Sjulian error = ng_con_nodes(here, con->ourhook, node2, con->peerhook); 124052419Sjulian break; 124152419Sjulian } 124252419Sjulian case NGM_NAME: 124352419Sjulian { 124452419Sjulian struct ngm_name *const nam = (struct ngm_name *) msg->data; 124552419Sjulian 124652419Sjulian if (msg->header.arglen != sizeof(*nam)) { 124752419Sjulian TRAP_ERROR; 124852419Sjulian return (EINVAL); 124952419Sjulian } 125052419Sjulian nam->name[sizeof(nam->name) - 1] = '\0'; 125152419Sjulian error = ng_name_node(here, nam->name); 125252419Sjulian break; 125352419Sjulian } 125452419Sjulian case NGM_RMHOOK: 125552419Sjulian { 125652419Sjulian struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 125752419Sjulian hook_p hook; 125852419Sjulian 125952419Sjulian if (msg->header.arglen != sizeof(*rmh)) { 126052419Sjulian TRAP_ERROR; 126152419Sjulian return (EINVAL); 126252419Sjulian } 126352419Sjulian rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 126454096Sarchie if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 126552419Sjulian ng_destroy_hook(hook); 126652419Sjulian break; 126752419Sjulian } 126852419Sjulian case NGM_NODEINFO: 126952419Sjulian { 127052419Sjulian struct nodeinfo *ni; 127152419Sjulian struct ng_mesg *rp; 127252419Sjulian 127352419Sjulian /* Get response struct */ 127452419Sjulian if (resp == NULL) { 127552419Sjulian error = EINVAL; 127652419Sjulian break; 127752419Sjulian } 127852419Sjulian NG_MKRESPONSE(rp, msg, sizeof(*ni), M_NOWAIT); 127952419Sjulian if (rp == NULL) { 128052419Sjulian error = ENOMEM; 128152419Sjulian break; 128252419Sjulian } 128352419Sjulian 128452419Sjulian /* Fill in node info */ 128552419Sjulian ni = (struct nodeinfo *) rp->data; 128652419Sjulian if (here->name != NULL) 128752419Sjulian strncpy(ni->name, here->name, NG_NODELEN); 128852419Sjulian strncpy(ni->type, here->type->name, NG_TYPELEN); 128952722Sjulian ni->id = ng_node2ID(here); 129052419Sjulian ni->hooks = here->numhooks; 129152419Sjulian *resp = rp; 129252419Sjulian break; 129352419Sjulian } 129452419Sjulian case NGM_LISTHOOKS: 129552419Sjulian { 129652419Sjulian const int nhooks = here->numhooks; 129752419Sjulian struct hooklist *hl; 129852419Sjulian struct nodeinfo *ni; 129952419Sjulian struct ng_mesg *rp; 130052419Sjulian hook_p hook; 130152419Sjulian 130252419Sjulian /* Get response struct */ 130352419Sjulian if (resp == NULL) { 130452419Sjulian error = EINVAL; 130552419Sjulian break; 130652419Sjulian } 130752419Sjulian NG_MKRESPONSE(rp, msg, sizeof(*hl) 130852419Sjulian + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 130952419Sjulian if (rp == NULL) { 131052419Sjulian error = ENOMEM; 131152419Sjulian break; 131252419Sjulian } 131352419Sjulian hl = (struct hooklist *) rp->data; 131452419Sjulian ni = &hl->nodeinfo; 131552419Sjulian 131652419Sjulian /* Fill in node info */ 131752419Sjulian if (here->name) 131852419Sjulian strncpy(ni->name, here->name, NG_NODELEN); 131952419Sjulian strncpy(ni->type, here->type->name, NG_TYPELEN); 132052722Sjulian ni->id = ng_node2ID(here); 132152419Sjulian 132252419Sjulian /* Cycle through the linked list of hooks */ 132352419Sjulian ni->hooks = 0; 132452419Sjulian LIST_FOREACH(hook, &here->hooks, hooks) { 132552419Sjulian struct linkinfo *const link = &hl->link[ni->hooks]; 132652419Sjulian 132752419Sjulian if (ni->hooks >= nhooks) { 132852419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 132952419Sjulian __FUNCTION__, "hooks"); 133052419Sjulian break; 133152419Sjulian } 133252419Sjulian if ((hook->flags & HK_INVALID) != 0) 133352419Sjulian continue; 133452419Sjulian strncpy(link->ourhook, hook->name, NG_HOOKLEN); 133552419Sjulian strncpy(link->peerhook, hook->peer->name, NG_HOOKLEN); 133652419Sjulian if (hook->peer->node->name != NULL) 133752419Sjulian strncpy(link->nodeinfo.name, 133852419Sjulian hook->peer->node->name, NG_NODELEN); 133952419Sjulian strncpy(link->nodeinfo.type, 134052419Sjulian hook->peer->node->type->name, NG_TYPELEN); 134152722Sjulian link->nodeinfo.id = ng_node2ID(hook->peer->node); 134252419Sjulian link->nodeinfo.hooks = hook->peer->node->numhooks; 134352419Sjulian ni->hooks++; 134452419Sjulian } 134552419Sjulian *resp = rp; 134652419Sjulian break; 134752419Sjulian } 134852419Sjulian 134952419Sjulian case NGM_LISTNAMES: 135052419Sjulian case NGM_LISTNODES: 135152419Sjulian { 135252419Sjulian const int unnamed = (msg->header.cmd == NGM_LISTNODES); 135352419Sjulian struct namelist *nl; 135452419Sjulian struct ng_mesg *rp; 135552419Sjulian node_p node; 135652419Sjulian int num = 0; 135752419Sjulian 135852419Sjulian if (resp == NULL) { 135952419Sjulian error = EINVAL; 136052419Sjulian break; 136152419Sjulian } 136252419Sjulian 136352419Sjulian /* Count number of nodes */ 136452419Sjulian LIST_FOREACH(node, &nodelist, nodes) { 136552419Sjulian if (unnamed || node->name != NULL) 136652419Sjulian num++; 136752419Sjulian } 136852419Sjulian 136952419Sjulian /* Get response struct */ 137052419Sjulian if (resp == NULL) { 137152419Sjulian error = EINVAL; 137252419Sjulian break; 137352419Sjulian } 137452419Sjulian NG_MKRESPONSE(rp, msg, sizeof(*nl) 137552419Sjulian + (num * sizeof(struct nodeinfo)), M_NOWAIT); 137652419Sjulian if (rp == NULL) { 137752419Sjulian error = ENOMEM; 137852419Sjulian break; 137952419Sjulian } 138052419Sjulian nl = (struct namelist *) rp->data; 138152419Sjulian 138252419Sjulian /* Cycle through the linked list of nodes */ 138352419Sjulian nl->numnames = 0; 138452419Sjulian LIST_FOREACH(node, &nodelist, nodes) { 138552419Sjulian struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; 138652419Sjulian 138752419Sjulian if (nl->numnames >= num) { 138852419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 138952419Sjulian __FUNCTION__, "nodes"); 139052419Sjulian break; 139152419Sjulian } 139252419Sjulian if ((node->flags & NG_INVALID) != 0) 139352419Sjulian continue; 139452419Sjulian if (!unnamed && node->name == NULL) 139552419Sjulian continue; 139652419Sjulian if (node->name != NULL) 139752419Sjulian strncpy(np->name, node->name, NG_NODELEN); 139852419Sjulian strncpy(np->type, node->type->name, NG_TYPELEN); 139952722Sjulian np->id = ng_node2ID(node); 140052419Sjulian np->hooks = node->numhooks; 140152419Sjulian nl->numnames++; 140252419Sjulian } 140352419Sjulian *resp = rp; 140452419Sjulian break; 140552419Sjulian } 140652419Sjulian 140752419Sjulian case NGM_LISTTYPES: 140852419Sjulian { 140952419Sjulian struct typelist *tl; 141052419Sjulian struct ng_mesg *rp; 141152419Sjulian struct ng_type *type; 141252419Sjulian int num = 0; 141352419Sjulian 141452419Sjulian if (resp == NULL) { 141552419Sjulian error = EINVAL; 141652419Sjulian break; 141752419Sjulian } 141852419Sjulian 141952419Sjulian /* Count number of types */ 142052419Sjulian LIST_FOREACH(type, &typelist, types) 142152419Sjulian num++; 142252419Sjulian 142352419Sjulian /* Get response struct */ 142452419Sjulian if (resp == NULL) { 142552419Sjulian error = EINVAL; 142652419Sjulian break; 142752419Sjulian } 142852419Sjulian NG_MKRESPONSE(rp, msg, sizeof(*tl) 142952419Sjulian + (num * sizeof(struct typeinfo)), M_NOWAIT); 143052419Sjulian if (rp == NULL) { 143152419Sjulian error = ENOMEM; 143252419Sjulian break; 143352419Sjulian } 143452419Sjulian tl = (struct typelist *) rp->data; 143552419Sjulian 143652419Sjulian /* Cycle through the linked list of types */ 143752419Sjulian tl->numtypes = 0; 143852419Sjulian LIST_FOREACH(type, &typelist, types) { 143952419Sjulian struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 144052419Sjulian 144152419Sjulian if (tl->numtypes >= num) { 144252419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 144352419Sjulian __FUNCTION__, "types"); 144452419Sjulian break; 144552419Sjulian } 144659879Sarchie strncpy(tp->type_name, type->name, NG_TYPELEN); 144752419Sjulian tp->numnodes = type->refs; 144852419Sjulian tl->numtypes++; 144952419Sjulian } 145052419Sjulian *resp = rp; 145152419Sjulian break; 145252419Sjulian } 145352419Sjulian 145453913Sarchie case NGM_BINARY2ASCII: 145553913Sarchie { 145664510Sarchie int bufSize = 20 * 1024; /* XXX hard coded constant */ 145753913Sarchie const struct ng_parse_type *argstype; 145853913Sarchie const struct ng_cmdlist *c; 145953913Sarchie struct ng_mesg *rp, *binary, *ascii; 146053913Sarchie 146153913Sarchie /* Data area must contain a valid netgraph message */ 146253913Sarchie binary = (struct ng_mesg *)msg->data; 146353913Sarchie if (msg->header.arglen < sizeof(struct ng_mesg) 146453913Sarchie || msg->header.arglen - sizeof(struct ng_mesg) 146553913Sarchie < binary->header.arglen) { 146653913Sarchie error = EINVAL; 146753913Sarchie break; 146853913Sarchie } 146953913Sarchie 147053913Sarchie /* Get a response message with lots of room */ 147153913Sarchie NG_MKRESPONSE(rp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 147253913Sarchie if (rp == NULL) { 147353913Sarchie error = ENOMEM; 147453913Sarchie break; 147553913Sarchie } 147653913Sarchie ascii = (struct ng_mesg *)rp->data; 147753913Sarchie 147853913Sarchie /* Copy binary message header to response message payload */ 147953913Sarchie bcopy(binary, ascii, sizeof(*binary)); 148053913Sarchie 148153913Sarchie /* Find command by matching typecookie and command number */ 148253913Sarchie for (c = here->type->cmdlist; 148353913Sarchie c != NULL && c->name != NULL; c++) { 148453913Sarchie if (binary->header.typecookie == c->cookie 148553913Sarchie && binary->header.cmd == c->cmd) 148653913Sarchie break; 148753913Sarchie } 148853913Sarchie if (c == NULL || c->name == NULL) { 148953913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 149053913Sarchie if (binary->header.typecookie == c->cookie 149153913Sarchie && binary->header.cmd == c->cmd) 149253913Sarchie break; 149353913Sarchie } 149453913Sarchie if (c->name == NULL) { 149553913Sarchie FREE(rp, M_NETGRAPH); 149653913Sarchie error = ENOSYS; 149753913Sarchie break; 149853913Sarchie } 149953913Sarchie } 150053913Sarchie 150153913Sarchie /* Convert command name to ASCII */ 150253913Sarchie snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 150353913Sarchie "%s", c->name); 150453913Sarchie 150553913Sarchie /* Convert command arguments to ASCII */ 150653913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 150753913Sarchie c->respType : c->mesgType; 150853913Sarchie if (argstype == NULL) 150953913Sarchie *ascii->data = '\0'; 151053913Sarchie else { 151153913Sarchie if ((error = ng_unparse(argstype, 151253913Sarchie (u_char *)binary->data, 151353913Sarchie ascii->data, bufSize)) != 0) { 151453913Sarchie FREE(rp, M_NETGRAPH); 151553913Sarchie break; 151653913Sarchie } 151753913Sarchie } 151853913Sarchie 151953913Sarchie /* Return the result as struct ng_mesg plus ASCII string */ 152053913Sarchie bufSize = strlen(ascii->data) + 1; 152153913Sarchie ascii->header.arglen = bufSize; 152253913Sarchie rp->header.arglen = sizeof(*ascii) + bufSize; 152353913Sarchie *resp = rp; 152453913Sarchie break; 152553913Sarchie } 152653913Sarchie 152753913Sarchie case NGM_ASCII2BINARY: 152853913Sarchie { 152953913Sarchie int bufSize = 2000; /* XXX hard coded constant */ 153053913Sarchie const struct ng_cmdlist *c; 153153913Sarchie const struct ng_parse_type *argstype; 153253913Sarchie struct ng_mesg *rp, *ascii, *binary; 153359178Sarchie int off = 0; 153453913Sarchie 153553913Sarchie /* Data area must contain at least a struct ng_mesg + '\0' */ 153653913Sarchie ascii = (struct ng_mesg *)msg->data; 153753913Sarchie if (msg->header.arglen < sizeof(*ascii) + 1 153853913Sarchie || ascii->header.arglen < 1 153953913Sarchie || msg->header.arglen 154053913Sarchie < sizeof(*ascii) + ascii->header.arglen) { 154153913Sarchie error = EINVAL; 154253913Sarchie break; 154353913Sarchie } 154453913Sarchie ascii->data[ascii->header.arglen - 1] = '\0'; 154553913Sarchie 154653913Sarchie /* Get a response message with lots of room */ 154753913Sarchie NG_MKRESPONSE(rp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 154853913Sarchie if (rp == NULL) { 154953913Sarchie error = ENOMEM; 155053913Sarchie break; 155153913Sarchie } 155253913Sarchie binary = (struct ng_mesg *)rp->data; 155353913Sarchie 155453913Sarchie /* Copy ASCII message header to response message payload */ 155553913Sarchie bcopy(ascii, binary, sizeof(*ascii)); 155653913Sarchie 155753913Sarchie /* Find command by matching ASCII command string */ 155853913Sarchie for (c = here->type->cmdlist; 155953913Sarchie c != NULL && c->name != NULL; c++) { 156053913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 156153913Sarchie break; 156253913Sarchie } 156353913Sarchie if (c == NULL || c->name == NULL) { 156453913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 156553913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 156653913Sarchie break; 156753913Sarchie } 156853913Sarchie if (c->name == NULL) { 156953913Sarchie FREE(rp, M_NETGRAPH); 157053913Sarchie error = ENOSYS; 157153913Sarchie break; 157253913Sarchie } 157353913Sarchie } 157453913Sarchie 157553913Sarchie /* Convert command name to binary */ 157653913Sarchie binary->header.cmd = c->cmd; 157753913Sarchie binary->header.typecookie = c->cookie; 157853913Sarchie 157953913Sarchie /* Convert command arguments to binary */ 158053913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 158153913Sarchie c->respType : c->mesgType; 158253913Sarchie if (argstype == NULL) 158353913Sarchie bufSize = 0; 158453913Sarchie else { 158553913Sarchie if ((error = ng_parse(argstype, ascii->data, 158653913Sarchie &off, (u_char *)binary->data, &bufSize)) != 0) { 158753913Sarchie FREE(rp, M_NETGRAPH); 158853913Sarchie break; 158953913Sarchie } 159053913Sarchie } 159153913Sarchie 159253913Sarchie /* Return the result */ 159353913Sarchie binary->header.arglen = bufSize; 159453913Sarchie rp->header.arglen = sizeof(*binary) + bufSize; 159553913Sarchie *resp = rp; 159653913Sarchie break; 159753913Sarchie } 159853913Sarchie 159962471Sphk case NGM_TEXT_CONFIG: 160052419Sjulian case NGM_TEXT_STATUS: 160152419Sjulian /* 160252419Sjulian * This one is tricky as it passes the command down to the 160352419Sjulian * actual node, even though it is a generic type command. 160452419Sjulian * This means we must assume that the msg is already freed 160552419Sjulian * when control passes back to us. 160652419Sjulian */ 160752419Sjulian if (resp == NULL) { 160852419Sjulian error = EINVAL; 160952419Sjulian break; 161052419Sjulian } 161152419Sjulian if (here->type->rcvmsg != NULL) 161259728Sjulian return((*here->type->rcvmsg)(here, msg, retaddr, 161359728Sjulian resp, lasthook)); 161452419Sjulian /* Fall through if rcvmsg not supported */ 161552419Sjulian default: 161652419Sjulian TRAP_ERROR; 161752419Sjulian error = EINVAL; 161852419Sjulian } 161952419Sjulian FREE(msg, M_NETGRAPH); 162052419Sjulian return (error); 162152419Sjulian} 162252419Sjulian 162352419Sjulian/* 162452419Sjulian * Send a data packet to a node. If the recipient has no 162552419Sjulian * 'receive data' method, then silently discard the packet. 162652419Sjulian */ 162752419Sjulianint 162859728Sjulianng_send_data(hook_p hook, struct mbuf *m, meta_p meta, 162959728Sjulian struct mbuf **ret_m, meta_p *ret_meta) 163052419Sjulian{ 163159728Sjulian ng_rcvdata_t *rcvdata; 163252419Sjulian int error; 163352419Sjulian 163453403Sarchie CHECK_DATA_MBUF(m); 163552419Sjulian if (hook && (hook->flags & HK_INVALID) == 0) { 163652419Sjulian rcvdata = hook->peer->node->type->rcvdata; 163752419Sjulian if (rcvdata != NULL) 163859728Sjulian error = (*rcvdata)(hook->peer, m, meta, 163959728Sjulian ret_m, ret_meta); 164052419Sjulian else { 164152419Sjulian error = 0; 164252419Sjulian NG_FREE_DATA(m, meta); 164352419Sjulian } 164452419Sjulian } else { 164552419Sjulian TRAP_ERROR; 164652419Sjulian error = ENOTCONN; 164752419Sjulian NG_FREE_DATA(m, meta); 164852419Sjulian } 164952419Sjulian return (error); 165052419Sjulian} 165152419Sjulian 165252419Sjulian/* 165352419Sjulian * Send a queued data packet to a node. If the recipient has no 165452419Sjulian * 'receive queued data' method, then try the 'receive data' method above. 165552419Sjulian */ 165652419Sjulianint 165759728Sjulianng_send_dataq(hook_p hook, struct mbuf *m, meta_p meta, 165859728Sjulian struct mbuf **ret_m, meta_p *ret_meta) 165952419Sjulian{ 166059728Sjulian ng_rcvdata_t *rcvdata; 166152419Sjulian int error; 166252419Sjulian 166353403Sarchie CHECK_DATA_MBUF(m); 166452419Sjulian if (hook && (hook->flags & HK_INVALID) == 0) { 166559728Sjulian rcvdata = hook->peer->node->type->rcvdataq; 166659728Sjulian if (rcvdata != NULL) 166759728Sjulian error = (*rcvdata)(hook->peer, m, meta, 166859728Sjulian ret_m, ret_meta); 166952419Sjulian else { 167059728Sjulian rcvdata = hook->peer->node->type->rcvdata; 167159728Sjulian if (rcvdata != NULL) { 167259728Sjulian error = (*rcvdata)(hook->peer, m, meta, 167359728Sjulian ret_m, ret_meta); 167459728Sjulian } else { 167559728Sjulian error = 0; 167659728Sjulian NG_FREE_DATA(m, meta); 167759728Sjulian } 167852419Sjulian } 167952419Sjulian } else { 168052419Sjulian TRAP_ERROR; 168152419Sjulian error = ENOTCONN; 168252419Sjulian NG_FREE_DATA(m, meta); 168352419Sjulian } 168452419Sjulian return (error); 168552419Sjulian} 168652419Sjulian 168759879Sarchie/* 168859879Sarchie * Copy a 'meta'. 168959879Sarchie * 169059879Sarchie * Returns new meta, or NULL if original meta is NULL or ENOMEM. 169159879Sarchie */ 169259879Sarchiemeta_p 169359879Sarchieng_copy_meta(meta_p meta) 169459879Sarchie{ 169559879Sarchie meta_p meta2; 169659879Sarchie 169759879Sarchie if (meta == NULL) 169859879Sarchie return (NULL); 169959879Sarchie MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH, M_NOWAIT); 170059879Sarchie if (meta2 == NULL) 170159879Sarchie return (NULL); 170259879Sarchie meta2->allocated_len = meta->used_len; 170359879Sarchie bcopy(meta, meta2, meta->used_len); 170459879Sarchie return (meta2); 170559879Sarchie} 170659879Sarchie 170752419Sjulian/************************************************************************ 170852419Sjulian Module routines 170952419Sjulian************************************************************************/ 171052419Sjulian 171152419Sjulian/* 171252419Sjulian * Handle the loading/unloading of a netgraph node type module 171352419Sjulian */ 171452419Sjulianint 171552419Sjulianng_mod_event(module_t mod, int event, void *data) 171652419Sjulian{ 171752419Sjulian struct ng_type *const type = data; 171852419Sjulian int s, error = 0; 171952419Sjulian 172052419Sjulian switch (event) { 172152419Sjulian case MOD_LOAD: 172252419Sjulian 172352419Sjulian /* Register new netgraph node type */ 172452419Sjulian s = splnet(); 172552419Sjulian if ((error = ng_newtype(type)) != 0) { 172652419Sjulian splx(s); 172752419Sjulian break; 172852419Sjulian } 172952419Sjulian 173052419Sjulian /* Call type specific code */ 173152419Sjulian if (type->mod_event != NULL) 173252419Sjulian if ((error = (*type->mod_event)(mod, event, data)) != 0) 173352419Sjulian LIST_REMOVE(type, types); 173452419Sjulian splx(s); 173552419Sjulian break; 173652419Sjulian 173752419Sjulian case MOD_UNLOAD: 173852419Sjulian s = splnet(); 173952419Sjulian if (type->refs != 0) /* make sure no nodes exist! */ 174052419Sjulian error = EBUSY; 174152419Sjulian else { 174252419Sjulian if (type->mod_event != NULL) { /* check with type */ 174352419Sjulian error = (*type->mod_event)(mod, event, data); 174452419Sjulian if (error != 0) { /* type refuses.. */ 174552419Sjulian splx(s); 174652419Sjulian break; 174752419Sjulian } 174852419Sjulian } 174952419Sjulian LIST_REMOVE(type, types); 175052419Sjulian } 175152419Sjulian splx(s); 175252419Sjulian break; 175352419Sjulian 175452419Sjulian default: 175552419Sjulian if (type->mod_event != NULL) 175652419Sjulian error = (*type->mod_event)(mod, event, data); 175752419Sjulian else 175852419Sjulian error = 0; /* XXX ? */ 175952419Sjulian break; 176052419Sjulian } 176152419Sjulian return (error); 176252419Sjulian} 176352419Sjulian 176452419Sjulian/* 176552419Sjulian * Handle loading and unloading for this code. 176652419Sjulian * The only thing we need to link into is the NETISR strucure. 176752419Sjulian */ 176852419Sjulianstatic int 176952419Sjulianngb_mod_event(module_t mod, int event, void *data) 177052419Sjulian{ 177152419Sjulian int s, error = 0; 177252419Sjulian 177352419Sjulian switch (event) { 177452419Sjulian case MOD_LOAD: 177552419Sjulian /* Register line discipline */ 177652419Sjulian s = splimp(); 177752419Sjulian error = register_netisr(NETISR_NETGRAPH, ngintr); 177852419Sjulian splx(s); 177952419Sjulian break; 178052419Sjulian case MOD_UNLOAD: 178152419Sjulian /* You cant unload it because an interface may be using it. */ 178252419Sjulian error = EBUSY; 178352419Sjulian break; 178452419Sjulian default: 178552419Sjulian error = EOPNOTSUPP; 178652419Sjulian break; 178752419Sjulian } 178852419Sjulian return (error); 178952419Sjulian} 179052419Sjulian 179152419Sjulianstatic moduledata_t netgraph_mod = { 179252419Sjulian "netgraph", 179352419Sjulian ngb_mod_event, 179452419Sjulian (NULL) 179552419Sjulian}; 179652419SjulianDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 179752419Sjulian 179852419Sjulian/************************************************************************ 179952419Sjulian Queueing routines 180052419Sjulian************************************************************************/ 180152419Sjulian 180252419Sjulian/* The structure for queueing across ISR switches */ 180352419Sjulianstruct ng_queue_entry { 180452419Sjulian u_long flags; 180552419Sjulian struct ng_queue_entry *next; 180652419Sjulian union { 180752419Sjulian struct { 180852419Sjulian hook_p da_hook; /* target hook */ 180952419Sjulian struct mbuf *da_m; 181052419Sjulian meta_p da_meta; 181152419Sjulian } data; 181252419Sjulian struct { 181352419Sjulian struct ng_mesg *msg_msg; 181452419Sjulian node_p msg_node; 181552419Sjulian void *msg_retaddr; 181659728Sjulian hook_p msg_lasthook; 181752419Sjulian } msg; 181852419Sjulian } body; 181952419Sjulian}; 182052419Sjulian#define NGQF_DATA 0x01 /* the queue element is data */ 182152419Sjulian#define NGQF_MESG 0x02 /* the queue element is a message */ 182252419Sjulian 182352419Sjulianstatic struct ng_queue_entry *ngqbase; /* items to be unqueued */ 182452419Sjulianstatic struct ng_queue_entry *ngqlast; /* last item queued */ 182552419Sjulianstatic const int ngqroom = 64; /* max items to queue */ 182652419Sjulianstatic int ngqsize; /* number of items in queue */ 182752419Sjulian 182852419Sjulianstatic struct ng_queue_entry *ngqfree; /* free ones */ 182952419Sjulianstatic const int ngqfreemax = 16;/* cache at most this many */ 183052419Sjulianstatic int ngqfreesize; /* number of cached entries */ 183152419Sjulian 183252419Sjulian/* 183352419Sjulian * Get a queue entry 183452419Sjulian */ 183552419Sjulianstatic struct ng_queue_entry * 183652419Sjulianng_getqblk(void) 183752419Sjulian{ 183852419Sjulian register struct ng_queue_entry *q; 183952419Sjulian int s; 184052419Sjulian 184152419Sjulian /* Could be guarding against tty ints or whatever */ 184252419Sjulian s = splhigh(); 184352419Sjulian 184452419Sjulian /* Try get a cached queue block, or else allocate a new one */ 184552419Sjulian if ((q = ngqfree) == NULL) { 184652419Sjulian splx(s); 184752419Sjulian if (ngqsize < ngqroom) { /* don't worry about races */ 184852419Sjulian MALLOC(q, struct ng_queue_entry *, 184952419Sjulian sizeof(*q), M_NETGRAPH, M_NOWAIT); 185052419Sjulian } 185152419Sjulian } else { 185252419Sjulian ngqfree = q->next; 185352419Sjulian ngqfreesize--; 185452419Sjulian splx(s); 185552419Sjulian } 185652419Sjulian return (q); 185752419Sjulian} 185852419Sjulian 185952419Sjulian/* 186052419Sjulian * Release a queue entry 186152419Sjulian */ 186252419Sjulian#define RETURN_QBLK(q) \ 186352419Sjuliando { \ 186452419Sjulian int s; \ 186552419Sjulian if (ngqfreesize < ngqfreemax) { /* don't worry about races */ \ 186652419Sjulian s = splhigh(); \ 186752419Sjulian (q)->next = ngqfree; \ 186852419Sjulian ngqfree = (q); \ 186952419Sjulian ngqfreesize++; \ 187052419Sjulian splx(s); \ 187152419Sjulian } else { \ 187252419Sjulian FREE((q), M_NETGRAPH); \ 187352419Sjulian } \ 187452419Sjulian} while (0) 187552419Sjulian 187652419Sjulian/* 187752419Sjulian * Running at a raised (but we don't know which) processor priority level, 187852419Sjulian * put the data onto a queue to be picked up by another PPL (probably splnet) 187952419Sjulian */ 188052419Sjulianint 188152419Sjulianng_queue_data(hook_p hook, struct mbuf *m, meta_p meta) 188252419Sjulian{ 188352419Sjulian struct ng_queue_entry *q; 188452419Sjulian int s; 188552419Sjulian 188652419Sjulian if (hook == NULL) { 188752419Sjulian NG_FREE_DATA(m, meta); 188852419Sjulian return (0); 188952419Sjulian } 189052419Sjulian if ((q = ng_getqblk()) == NULL) { 189152419Sjulian NG_FREE_DATA(m, meta); 189252419Sjulian return (ENOBUFS); 189352419Sjulian } 189452419Sjulian 189552419Sjulian /* Fill out the contents */ 189652419Sjulian q->flags = NGQF_DATA; 189752419Sjulian q->next = NULL; 189852419Sjulian q->body.data.da_hook = hook; 189952419Sjulian q->body.data.da_m = m; 190052419Sjulian q->body.data.da_meta = meta; 190152419Sjulian hook->refs++; /* don't let it go away while on the queue */ 190252419Sjulian 190352419Sjulian /* Put it on the queue */ 190452419Sjulian s = splhigh(); 190552419Sjulian if (ngqbase) { 190652419Sjulian ngqlast->next = q; 190752419Sjulian } else { 190852419Sjulian ngqbase = q; 190952419Sjulian } 191052419Sjulian ngqlast = q; 191152419Sjulian ngqsize++; 191252419Sjulian splx(s); 191352419Sjulian 191452419Sjulian /* Schedule software interrupt to handle it later */ 191552419Sjulian schednetisr(NETISR_NETGRAPH); 191652419Sjulian return (0); 191752419Sjulian} 191852419Sjulian 191952419Sjulian/* 192052419Sjulian * Running at a raised (but we don't know which) processor priority level, 192152419Sjulian * put the msg onto a queue to be picked up by another PPL (probably splnet) 192252419Sjulian */ 192352419Sjulianint 192458013Sarchieng_queue_msg(node_p here, struct ng_mesg *msg, const char *address) 192552419Sjulian{ 192652419Sjulian register struct ng_queue_entry *q; 192752419Sjulian int s; 192852419Sjulian node_p dest = NULL; 192952419Sjulian char *retaddr = NULL; 193052419Sjulian int error; 193159728Sjulian hook_p lasthook = NULL; 193252419Sjulian 193352722Sjulian /* Find the target node. */ 193459728Sjulian error = ng_path2node(here, address, &dest, &retaddr, &lasthook); 193552419Sjulian if (error) { 193652419Sjulian FREE(msg, M_NETGRAPH); 193752419Sjulian return (error); 193852419Sjulian } 193952419Sjulian if ((q = ng_getqblk()) == NULL) { 194052419Sjulian FREE(msg, M_NETGRAPH); 194152419Sjulian if (retaddr) 194252419Sjulian FREE(retaddr, M_NETGRAPH); 194352419Sjulian return (ENOBUFS); 194452419Sjulian } 194552419Sjulian 194652419Sjulian /* Fill out the contents */ 194752419Sjulian q->flags = NGQF_MESG; 194852419Sjulian q->next = NULL; 194952419Sjulian q->body.msg.msg_node = dest; 195052419Sjulian q->body.msg.msg_msg = msg; 195152419Sjulian q->body.msg.msg_retaddr = retaddr; 195259728Sjulian q->body.msg.msg_lasthook = lasthook; 195352419Sjulian dest->refs++; /* don't let it go away while on the queue */ 195459728Sjulian if (lasthook) 195559728Sjulian lasthook->refs++; /* same for the hook */ 195652419Sjulian 195752419Sjulian /* Put it on the queue */ 195852419Sjulian s = splhigh(); 195952419Sjulian if (ngqbase) { 196052419Sjulian ngqlast->next = q; 196152419Sjulian } else { 196252419Sjulian ngqbase = q; 196352419Sjulian } 196452419Sjulian ngqlast = q; 196552419Sjulian ngqsize++; 196652419Sjulian splx(s); 196752419Sjulian 196852419Sjulian /* Schedule software interrupt to handle it later */ 196952419Sjulian schednetisr(NETISR_NETGRAPH); 197052419Sjulian return (0); 197152419Sjulian} 197252419Sjulian 197352419Sjulian/* 197452419Sjulian * Pick an item off the queue, process it, and dispose of the queue entry. 197552419Sjulian * Should be running at splnet. 197652419Sjulian */ 197752419Sjulianstatic void 197852419Sjulianngintr(void) 197952419Sjulian{ 198052419Sjulian hook_p hook; 198152419Sjulian struct ng_queue_entry *ngq; 198252419Sjulian struct mbuf *m; 198352419Sjulian meta_p meta; 198452419Sjulian void *retaddr; 198552419Sjulian struct ng_mesg *msg; 198652419Sjulian node_p node; 198752419Sjulian int error = 0; 198852419Sjulian int s; 198952419Sjulian 199052419Sjulian while (1) { 199152419Sjulian s = splhigh(); 199252419Sjulian if ((ngq = ngqbase)) { 199352419Sjulian ngqbase = ngq->next; 199452419Sjulian ngqsize--; 199552419Sjulian } 199652419Sjulian splx(s); 199752419Sjulian if (ngq == NULL) 199852419Sjulian return; 199952419Sjulian switch (ngq->flags) { 200052419Sjulian case NGQF_DATA: 200152419Sjulian hook = ngq->body.data.da_hook; 200252419Sjulian m = ngq->body.data.da_m; 200352419Sjulian meta = ngq->body.data.da_meta; 200452419Sjulian RETURN_QBLK(ngq); 200552419Sjulian NG_SEND_DATAQ(error, hook, m, meta); 200652419Sjulian ng_unref_hook(hook); 200752419Sjulian break; 200852419Sjulian case NGQF_MESG: 200952419Sjulian node = ngq->body.msg.msg_node; 201052419Sjulian msg = ngq->body.msg.msg_msg; 201152419Sjulian retaddr = ngq->body.msg.msg_retaddr; 201259728Sjulian hook = ngq->body.msg.msg_lasthook; 201352419Sjulian RETURN_QBLK(ngq); 201459728Sjulian if (hook) { 201559728Sjulian if ((hook->refs == 1) 201659728Sjulian || (hook->flags & HK_INVALID) != 0) { 201759728Sjulian /* If the hook only has one ref left 201859728Sjulian then we can't use it */ 201959728Sjulian ng_unref_hook(hook); 202059728Sjulian hook = NULL; 202159728Sjulian } else { 202259728Sjulian ng_unref_hook(hook); 202359728Sjulian } 202459728Sjulian } 202559728Sjulian /* similarly, if the node is a zombie.. */ 202652419Sjulian if (node->flags & NG_INVALID) { 202752419Sjulian FREE(msg, M_NETGRAPH); 202852419Sjulian } else { 202952419Sjulian CALL_MSG_HANDLER(error, node, msg, 203059728Sjulian retaddr, NULL, hook); 203152419Sjulian } 203252419Sjulian ng_unref(node); 203352419Sjulian if (retaddr) 203452419Sjulian FREE(retaddr, M_NETGRAPH); 203552419Sjulian break; 203652419Sjulian default: 203752419Sjulian RETURN_QBLK(ngq); 203852419Sjulian } 203952419Sjulian } 204052419Sjulian} 204152419Sjulian 204252419Sjulian 2043