ng_base.c revision 69922
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 * 3767506Sjulian * Authors: Julian Elischer <julian@freebsd.org> 3867506Sjulian * Archie Cobbs <archie@freebsd.org> 3952419Sjulian * 4052419Sjulian * $FreeBSD: head/sys/netgraph/ng_base.c 69922 2000-12-12 18:52:14Z julian $ 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); 8869922Sjulianstatic int ng_send_data_dont_queue(hook_p hook, struct mbuf *m, 8969922Sjulian meta_p meta, struct mbuf **ret_m, meta_p *ret_meta, 9069922Sjulian struct ng_mesg **resp); 9152419Sjulianstatic void ngintr(void); 9252419Sjulian 9352419Sjulian/* Our own netgraph malloc type */ 9452419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); 9552419Sjulian 9652419Sjulian/* Set this to Debugger("X") to catch all errors as they occur */ 9752419Sjulian#ifndef TRAP_ERROR 9852419Sjulian#define TRAP_ERROR 9952419Sjulian#endif 10052419Sjulian 10152722Sjulianstatic ng_ID_t nextID = 1; 10252722Sjulian 10353403Sarchie#ifdef INVARIANTS 10453403Sarchie#define CHECK_DATA_MBUF(m) do { \ 10553403Sarchie struct mbuf *n; \ 10653403Sarchie int total; \ 10753403Sarchie \ 10853403Sarchie if (((m)->m_flags & M_PKTHDR) == 0) \ 10953403Sarchie panic("%s: !PKTHDR", __FUNCTION__); \ 11053403Sarchie for (total = 0, n = (m); n != NULL; n = n->m_next) \ 11153403Sarchie total += n->m_len; \ 11253403Sarchie if ((m)->m_pkthdr.len != total) { \ 11353403Sarchie panic("%s: %d != %d", \ 11453403Sarchie __FUNCTION__, (m)->m_pkthdr.len, total); \ 11553403Sarchie } \ 11653403Sarchie } while (0) 11753403Sarchie#else 11853403Sarchie#define CHECK_DATA_MBUF(m) 11953403Sarchie#endif 12052722Sjulian 12153403Sarchie 12252419Sjulian/************************************************************************ 12353913Sarchie Parse type definitions for generic messages 12453913Sarchie************************************************************************/ 12553913Sarchie 12653913Sarchie/* Handy structure parse type defining macro */ 12753913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ 12853913Sarchiestatic const struct ng_parse_struct_info \ 12953913Sarchie ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args; \ 13053913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = { \ 13153913Sarchie &ng_parse_struct_type, \ 13253913Sarchie &ng_ ## lo ## _type_info \ 13353913Sarchie} 13453913Sarchie 13553913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); 13653913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); 13753913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); 13853913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); 13953913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); 14053913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); 14153913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); 14253913Sarchie 14353913Sarchie/* Get length of an array when the length is stored as a 32 bit 14453913Sarchie value immediately preceeding the array -- as with struct namelist 14553913Sarchie and struct typelist. */ 14653913Sarchiestatic int 14753913Sarchieng_generic_list_getLength(const struct ng_parse_type *type, 14853913Sarchie const u_char *start, const u_char *buf) 14953913Sarchie{ 15053913Sarchie return *((const u_int32_t *)(buf - 4)); 15153913Sarchie} 15253913Sarchie 15353913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */ 15453913Sarchiestatic int 15553913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type, 15653913Sarchie const u_char *start, const u_char *buf) 15753913Sarchie{ 15853913Sarchie const struct hooklist *hl = (const struct hooklist *)start; 15953913Sarchie 16053913Sarchie return hl->nodeinfo.hooks; 16153913Sarchie} 16253913Sarchie 16353913Sarchie/* Array type for a variable length array of struct namelist */ 16453913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = { 16553913Sarchie &ng_generic_nodeinfo_type, 16653913Sarchie &ng_generic_list_getLength 16753913Sarchie}; 16853913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = { 16953913Sarchie &ng_parse_array_type, 17053913Sarchie &ng_nodeinfoarray_type_info 17153913Sarchie}; 17253913Sarchie 17353913Sarchie/* Array type for a variable length array of struct typelist */ 17453913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = { 17553913Sarchie &ng_generic_typeinfo_type, 17653913Sarchie &ng_generic_list_getLength 17753913Sarchie}; 17853913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = { 17953913Sarchie &ng_parse_array_type, 18053913Sarchie &ng_typeinfoarray_type_info 18153913Sarchie}; 18253913Sarchie 18353913Sarchie/* Array type for array of struct linkinfo in struct hooklist */ 18453913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { 18553913Sarchie &ng_generic_linkinfo_type, 18653913Sarchie &ng_generic_linkinfo_getLength 18753913Sarchie}; 18853913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = { 18953913Sarchie &ng_parse_array_type, 19053913Sarchie &ng_generic_linkinfo_array_type_info 19153913Sarchie}; 19253913Sarchie 19353913SarchieDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); 19453913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, 19553913Sarchie (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); 19653913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, 19753913Sarchie (&ng_generic_nodeinfoarray_type)); 19853913Sarchie 19953913Sarchie/* List of commands and how to convert arguments to/from ASCII */ 20053913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = { 20153913Sarchie { 20253913Sarchie NGM_GENERIC_COOKIE, 20353913Sarchie NGM_SHUTDOWN, 20453913Sarchie "shutdown", 20553913Sarchie NULL, 20653913Sarchie NULL 20753913Sarchie }, 20853913Sarchie { 20953913Sarchie NGM_GENERIC_COOKIE, 21053913Sarchie NGM_MKPEER, 21153913Sarchie "mkpeer", 21253913Sarchie &ng_generic_mkpeer_type, 21353913Sarchie NULL 21453913Sarchie }, 21553913Sarchie { 21653913Sarchie NGM_GENERIC_COOKIE, 21753913Sarchie NGM_CONNECT, 21853913Sarchie "connect", 21953913Sarchie &ng_generic_connect_type, 22053913Sarchie NULL 22153913Sarchie }, 22253913Sarchie { 22353913Sarchie NGM_GENERIC_COOKIE, 22453913Sarchie NGM_NAME, 22553913Sarchie "name", 22653913Sarchie &ng_generic_name_type, 22753913Sarchie NULL 22853913Sarchie }, 22953913Sarchie { 23053913Sarchie NGM_GENERIC_COOKIE, 23153913Sarchie NGM_RMHOOK, 23253913Sarchie "rmhook", 23353913Sarchie &ng_generic_rmhook_type, 23453913Sarchie NULL 23553913Sarchie }, 23653913Sarchie { 23753913Sarchie NGM_GENERIC_COOKIE, 23853913Sarchie NGM_NODEINFO, 23953913Sarchie "nodeinfo", 24053913Sarchie NULL, 24153913Sarchie &ng_generic_nodeinfo_type 24253913Sarchie }, 24353913Sarchie { 24453913Sarchie NGM_GENERIC_COOKIE, 24553913Sarchie NGM_LISTHOOKS, 24653913Sarchie "listhooks", 24753913Sarchie NULL, 24853913Sarchie &ng_generic_hooklist_type 24953913Sarchie }, 25053913Sarchie { 25153913Sarchie NGM_GENERIC_COOKIE, 25253913Sarchie NGM_LISTNAMES, 25353913Sarchie "listnames", 25453913Sarchie NULL, 25553913Sarchie &ng_generic_listnodes_type /* same as NGM_LISTNODES */ 25653913Sarchie }, 25753913Sarchie { 25853913Sarchie NGM_GENERIC_COOKIE, 25953913Sarchie NGM_LISTNODES, 26053913Sarchie "listnodes", 26153913Sarchie NULL, 26253913Sarchie &ng_generic_listnodes_type 26353913Sarchie }, 26453913Sarchie { 26553913Sarchie NGM_GENERIC_COOKIE, 26653913Sarchie NGM_LISTTYPES, 26753913Sarchie "listtypes", 26853913Sarchie NULL, 26953913Sarchie &ng_generic_typeinfo_type 27053913Sarchie }, 27153913Sarchie { 27253913Sarchie NGM_GENERIC_COOKIE, 27362471Sphk NGM_TEXT_CONFIG, 27462471Sphk "textconfig", 27562471Sphk NULL, 27662471Sphk &ng_parse_string_type 27762471Sphk }, 27862471Sphk { 27962471Sphk NGM_GENERIC_COOKIE, 28053913Sarchie NGM_TEXT_STATUS, 28153913Sarchie "textstatus", 28253913Sarchie NULL, 28353913Sarchie &ng_parse_string_type 28453913Sarchie }, 28553913Sarchie { 28653913Sarchie NGM_GENERIC_COOKIE, 28753913Sarchie NGM_ASCII2BINARY, 28853913Sarchie "ascii2binary", 28953913Sarchie &ng_parse_ng_mesg_type, 29053913Sarchie &ng_parse_ng_mesg_type 29153913Sarchie }, 29253913Sarchie { 29353913Sarchie NGM_GENERIC_COOKIE, 29453913Sarchie NGM_BINARY2ASCII, 29553913Sarchie "binary2ascii", 29653913Sarchie &ng_parse_ng_mesg_type, 29753913Sarchie &ng_parse_ng_mesg_type 29853913Sarchie }, 29953913Sarchie { 0 } 30053913Sarchie}; 30153913Sarchie 30253913Sarchie/************************************************************************ 30352419Sjulian Node routines 30452419Sjulian************************************************************************/ 30552419Sjulian 30652419Sjulian/* 30752419Sjulian * Instantiate a node of the requested type 30852419Sjulian */ 30952419Sjulianint 31052419Sjulianng_make_node(const char *typename, node_p *nodepp) 31152419Sjulian{ 31252419Sjulian struct ng_type *type; 31352419Sjulian 31452419Sjulian /* Check that the type makes sense */ 31552419Sjulian if (typename == NULL) { 31652419Sjulian TRAP_ERROR; 31752419Sjulian return (EINVAL); 31852419Sjulian } 31952419Sjulian 32052419Sjulian /* Locate the node type */ 32152419Sjulian if ((type = ng_findtype(typename)) == NULL) { 32259875Speter char filename[NG_TYPELEN + 4]; 32352419Sjulian linker_file_t lf; 32452419Sjulian int error; 32552419Sjulian 32652419Sjulian /* Not found, try to load it as a loadable module */ 32769922Sjulian snprintf(filename, sizeof(filename), "/boot/kernel/ng_%s", typename); 32859875Speter error = linker_load_file(filename, &lf); 32952419Sjulian if (error != 0) 33052419Sjulian return (error); 33152419Sjulian lf->userrefs++; /* pretend loaded by the syscall */ 33252419Sjulian 33352419Sjulian /* Try again, as now the type should have linked itself in */ 33452419Sjulian if ((type = ng_findtype(typename)) == NULL) 33552419Sjulian return (ENXIO); 33652419Sjulian } 33752419Sjulian 33852419Sjulian /* Call the constructor */ 33952419Sjulian if (type->constructor != NULL) 34052419Sjulian return ((*type->constructor)(nodepp)); 34152419Sjulian else 34252419Sjulian return (ng_make_node_common(type, nodepp)); 34352419Sjulian} 34452419Sjulian 34552419Sjulian/* 34652419Sjulian * Generic node creation. Called by node constructors. 34752419Sjulian * The returned node has a reference count of 1. 34852419Sjulian */ 34952419Sjulianint 35052419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp) 35152419Sjulian{ 35252419Sjulian node_p node; 35352419Sjulian 35452419Sjulian /* Require the node type to have been already installed */ 35552419Sjulian if (ng_findtype(type->name) == NULL) { 35652419Sjulian TRAP_ERROR; 35752419Sjulian return (EINVAL); 35852419Sjulian } 35952419Sjulian 36052419Sjulian /* Make a node and try attach it to the type */ 36168876Sdwmalone MALLOC(node, node_p, sizeof(*node), M_NETGRAPH, M_NOWAIT | M_ZERO); 36252419Sjulian if (node == NULL) { 36352419Sjulian TRAP_ERROR; 36452419Sjulian return (ENOMEM); 36552419Sjulian } 36652419Sjulian node->type = type; 36752419Sjulian node->refs++; /* note reference */ 36852419Sjulian type->refs++; 36952419Sjulian 37052419Sjulian /* Link us into the node linked list */ 37152419Sjulian LIST_INSERT_HEAD(&nodelist, node, nodes); 37252419Sjulian 37352419Sjulian /* Initialize hook list for new node */ 37452419Sjulian LIST_INIT(&node->hooks); 37552419Sjulian 37652722Sjulian /* get an ID and put us in the hash chain */ 37752722Sjulian node->ID = nextID++; /* 137 per second for 1 year before wrap */ 37852722Sjulian LIST_INSERT_HEAD(&ID_hash[node->ID % ID_HASH_SIZE], node, idnodes); 37952722Sjulian 38052419Sjulian /* Done */ 38152419Sjulian *nodepp = node; 38252419Sjulian return (0); 38352419Sjulian} 38452419Sjulian 38552419Sjulian/* 38652419Sjulian * Forceably start the shutdown process on a node. Either call 38752419Sjulian * it's shutdown method, or do the default shutdown if there is 38852419Sjulian * no type-specific method. 38952419Sjulian * 39052419Sjulian * Persistent nodes must have a type-specific method which 39152419Sjulian * resets the NG_INVALID flag. 39252419Sjulian */ 39352419Sjulianvoid 39452419Sjulianng_rmnode(node_p node) 39552419Sjulian{ 39652419Sjulian /* Check if it's already shutting down */ 39752419Sjulian if ((node->flags & NG_INVALID) != 0) 39852419Sjulian return; 39952419Sjulian 40052419Sjulian /* Add an extra reference so it doesn't go away during this */ 40152419Sjulian node->refs++; 40252419Sjulian 40352419Sjulian /* Mark it invalid so any newcomers know not to try use it */ 40452419Sjulian node->flags |= NG_INVALID; 40552419Sjulian 40652419Sjulian /* Ask the type if it has anything to do in this case */ 40752419Sjulian if (node->type && node->type->shutdown) 40852419Sjulian (*node->type->shutdown)(node); 40952419Sjulian else { /* do the default thing */ 41052419Sjulian ng_unname(node); 41152419Sjulian ng_cutlinks(node); 41252419Sjulian ng_unref(node); 41352419Sjulian } 41452419Sjulian 41552419Sjulian /* Remove extra reference, possibly the last */ 41652419Sjulian ng_unref(node); 41752419Sjulian} 41852419Sjulian 41952419Sjulian/* 42052419Sjulian * Called by the destructor to remove any STANDARD external references 42152419Sjulian */ 42252419Sjulianvoid 42352419Sjulianng_cutlinks(node_p node) 42452419Sjulian{ 42552419Sjulian hook_p hook; 42652419Sjulian 42752419Sjulian /* Make sure that this is set to stop infinite loops */ 42852419Sjulian node->flags |= NG_INVALID; 42952419Sjulian 43052419Sjulian /* If we have sleepers, wake them up; they'll see NG_INVALID */ 43152419Sjulian if (node->sleepers) 43252419Sjulian wakeup(node); 43352419Sjulian 43452419Sjulian /* Notify all remaining connected nodes to disconnect */ 43552419Sjulian while ((hook = LIST_FIRST(&node->hooks)) != NULL) 43652419Sjulian ng_destroy_hook(hook); 43752419Sjulian} 43852419Sjulian 43952419Sjulian/* 44052419Sjulian * Remove a reference to the node, possibly the last 44152419Sjulian */ 44252419Sjulianvoid 44352419Sjulianng_unref(node_p node) 44452419Sjulian{ 44569519Sjulian int s; 44669519Sjulian 44769519Sjulian s = splhigh(); 44852419Sjulian if (--node->refs <= 0) { 44952419Sjulian node->type->refs--; 45052419Sjulian LIST_REMOVE(node, nodes); 45152722Sjulian LIST_REMOVE(node, idnodes); 45252419Sjulian FREE(node, M_NETGRAPH); 45352419Sjulian } 45469519Sjulian splx(s); 45552419Sjulian} 45652419Sjulian 45752419Sjulian/* 45852419Sjulian * Wait for a node to come ready. Returns a node with a reference count; 45952419Sjulian * don't forget to drop it when we are done with it using ng_release_node(). 46052419Sjulian */ 46152419Sjulianint 46252419Sjulianng_wait_node(node_p node, char *msg) 46352419Sjulian{ 46452419Sjulian int s, error = 0; 46552419Sjulian 46652419Sjulian if (msg == NULL) 46752419Sjulian msg = "netgraph"; 46852419Sjulian s = splnet(); 46952419Sjulian node->sleepers++; 47052419Sjulian node->refs++; /* the sleeping process counts as a reference */ 47152419Sjulian while ((node->flags & (NG_BUSY | NG_INVALID)) == NG_BUSY) 47252419Sjulian error = tsleep(node, (PZERO + 1) | PCATCH, msg, 0); 47352419Sjulian node->sleepers--; 47452419Sjulian if (node->flags & NG_INVALID) { 47552419Sjulian TRAP_ERROR; 47652419Sjulian error = ENXIO; 47752419Sjulian } else { 47853403Sarchie KASSERT(node->refs > 1, 47953403Sarchie ("%s: refs=%d", __FUNCTION__, node->refs)); 48052419Sjulian node->flags |= NG_BUSY; 48152419Sjulian } 48252419Sjulian splx(s); 48352419Sjulian 48452419Sjulian /* Release the reference we had on it */ 48552419Sjulian if (error != 0) 48652419Sjulian ng_unref(node); 48752419Sjulian return error; 48852419Sjulian} 48952419Sjulian 49052419Sjulian/* 49152419Sjulian * Release a node acquired via ng_wait_node() 49252419Sjulian */ 49352419Sjulianvoid 49452419Sjulianng_release_node(node_p node) 49552419Sjulian{ 49652419Sjulian /* Declare that we don't want it */ 49752419Sjulian node->flags &= ~NG_BUSY; 49852419Sjulian 49952419Sjulian /* If we have sleepers, then wake them up */ 50052419Sjulian if (node->sleepers) 50152419Sjulian wakeup(node); 50252419Sjulian 50352419Sjulian /* We also have a reference.. drop it too */ 50452419Sjulian ng_unref(node); 50552419Sjulian} 50652419Sjulian 50752419Sjulian/************************************************************************ 50852722Sjulian Node ID handling 50952722Sjulian************************************************************************/ 51052722Sjulianstatic node_p 51152722Sjulianng_ID2node(ng_ID_t ID) 51252722Sjulian{ 51352722Sjulian node_p np; 51452722Sjulian LIST_FOREACH(np, &ID_hash[ID % ID_HASH_SIZE], idnodes) { 51552722Sjulian if (np->ID == ID) 51652722Sjulian break; 51752722Sjulian } 51852722Sjulian return(np); 51952722Sjulian} 52052722Sjulian 52152722Sjulianng_ID_t 52252722Sjulianng_node2ID(node_p node) 52352722Sjulian{ 52452722Sjulian return (node->ID); 52552722Sjulian} 52652722Sjulian 52752722Sjulian/************************************************************************ 52852419Sjulian Node name handling 52952419Sjulian************************************************************************/ 53052419Sjulian 53152419Sjulian/* 53252419Sjulian * Assign a node a name. Once assigned, the name cannot be changed. 53352419Sjulian */ 53452419Sjulianint 53552419Sjulianng_name_node(node_p node, const char *name) 53652419Sjulian{ 53752419Sjulian int i; 53852419Sjulian 53952419Sjulian /* Check the name is valid */ 54052419Sjulian for (i = 0; i < NG_NODELEN + 1; i++) { 54152419Sjulian if (name[i] == '\0' || name[i] == '.' || name[i] == ':') 54252419Sjulian break; 54352419Sjulian } 54452419Sjulian if (i == 0 || name[i] != '\0') { 54552419Sjulian TRAP_ERROR; 54652419Sjulian return (EINVAL); 54752419Sjulian } 54852722Sjulian if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ 54952419Sjulian TRAP_ERROR; 55052419Sjulian return (EINVAL); 55152419Sjulian } 55252419Sjulian 55352419Sjulian /* Check the node isn't already named */ 55452419Sjulian if (node->name != NULL) { 55552419Sjulian TRAP_ERROR; 55652419Sjulian return (EISCONN); 55752419Sjulian } 55852419Sjulian 55952419Sjulian /* Check the name isn't already being used */ 56052419Sjulian if (ng_findname(node, name) != NULL) { 56152419Sjulian TRAP_ERROR; 56252419Sjulian return (EADDRINUSE); 56352419Sjulian } 56452419Sjulian 56552419Sjulian /* Allocate space and copy it */ 56663675Sarchie MALLOC(node->name, char *, strlen(name) + 1, M_NETGRAPH, M_NOWAIT); 56752419Sjulian if (node->name == NULL) { 56852419Sjulian TRAP_ERROR; 56952419Sjulian return (ENOMEM); 57052419Sjulian } 57152419Sjulian strcpy(node->name, name); 57252419Sjulian 57352419Sjulian /* The name counts as a reference */ 57452419Sjulian node->refs++; 57552419Sjulian return (0); 57652419Sjulian} 57752419Sjulian 57852419Sjulian/* 57952419Sjulian * Find a node by absolute name. The name should NOT end with ':' 58052419Sjulian * The name "." means "this node" and "[xxx]" means "the node 58152419Sjulian * with ID (ie, at address) xxx". 58252419Sjulian * 58352419Sjulian * Returns the node if found, else NULL. 58452419Sjulian */ 58552419Sjuliannode_p 58652419Sjulianng_findname(node_p this, const char *name) 58752419Sjulian{ 58852722Sjulian node_p node; 58952722Sjulian ng_ID_t temp; 59052419Sjulian 59152419Sjulian /* "." means "this node" */ 59252419Sjulian if (strcmp(name, ".") == 0) 59352419Sjulian return(this); 59452419Sjulian 59552419Sjulian /* Check for name-by-ID */ 59652722Sjulian if ((temp = ng_decodeidname(name)) != 0) { 59752722Sjulian return (ng_ID2node(temp)); 59852419Sjulian } 59952419Sjulian 60052419Sjulian /* Find node by name */ 60152419Sjulian LIST_FOREACH(node, &nodelist, nodes) { 60252419Sjulian if (node->name != NULL && strcmp(node->name, name) == 0) 60352419Sjulian break; 60452419Sjulian } 60552419Sjulian return (node); 60652419Sjulian} 60752419Sjulian 60852419Sjulian/* 60952722Sjulian * Decode a ID name, eg. "[f03034de]". Returns 0 if the 61052722Sjulian * string is not valid, otherwise returns the value. 61152419Sjulian */ 61252722Sjulianstatic ng_ID_t 61352419Sjulianng_decodeidname(const char *name) 61452419Sjulian{ 61552816Sarchie const int len = strlen(name); 61653648Sarchie char *eptr; 61752816Sarchie u_long val; 61852419Sjulian 61952816Sarchie /* Check for proper length, brackets, no leading junk */ 62052816Sarchie if (len < 3 || name[0] != '[' || name[len - 1] != ']' 62152951Sjulian || !isxdigit(name[1])) 62252816Sarchie return (0); 62352419Sjulian 62452816Sarchie /* Decode number */ 62552816Sarchie val = strtoul(name + 1, &eptr, 16); 62652951Sjulian if (eptr - name != len - 1 || val == ULONG_MAX || val == 0) 62753042Sjulian return ((ng_ID_t)0); 62852816Sarchie return (ng_ID_t)val; 62952419Sjulian} 63052419Sjulian 63152419Sjulian/* 63252419Sjulian * Remove a name from a node. This should only be called 63352419Sjulian * when shutting down and removing the node. 63452419Sjulian */ 63552419Sjulianvoid 63652419Sjulianng_unname(node_p node) 63752419Sjulian{ 63852419Sjulian if (node->name) { 63952419Sjulian FREE(node->name, M_NETGRAPH); 64052419Sjulian node->name = NULL; 64152419Sjulian ng_unref(node); 64252419Sjulian } 64352419Sjulian} 64452419Sjulian 64552419Sjulian/************************************************************************ 64652419Sjulian Hook routines 64752419Sjulian 64852419Sjulian Names are not optional. Hooks are always connected, except for a 64952419Sjulian brief moment within these routines. 65052419Sjulian 65152419Sjulian************************************************************************/ 65252419Sjulian 65352419Sjulian/* 65452419Sjulian * Remove a hook reference 65552419Sjulian */ 65652419Sjulianstatic void 65752419Sjulianng_unref_hook(hook_p hook) 65852419Sjulian{ 65969519Sjulian int s; 66069519Sjulian 66169519Sjulian s = splhigh(); 66252419Sjulian if (--hook->refs == 0) 66352419Sjulian FREE(hook, M_NETGRAPH); 66469519Sjulian splx(s); 66552419Sjulian} 66652419Sjulian 66752419Sjulian/* 66852419Sjulian * Add an unconnected hook to a node. Only used internally. 66952419Sjulian */ 67052419Sjulianstatic int 67152419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp) 67252419Sjulian{ 67352419Sjulian hook_p hook; 67452419Sjulian int error = 0; 67552419Sjulian 67652419Sjulian /* Check that the given name is good */ 67752419Sjulian if (name == NULL) { 67852419Sjulian TRAP_ERROR; 67952419Sjulian return (EINVAL); 68052419Sjulian } 68154096Sarchie if (ng_findhook(node, name) != NULL) { 68254096Sarchie TRAP_ERROR; 68354096Sarchie return (EEXIST); 68452419Sjulian } 68552419Sjulian 68652419Sjulian /* Allocate the hook and link it up */ 68768876Sdwmalone MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH, M_NOWAIT | M_ZERO); 68852419Sjulian if (hook == NULL) { 68952419Sjulian TRAP_ERROR; 69052419Sjulian return (ENOMEM); 69152419Sjulian } 69252419Sjulian hook->refs = 1; 69352419Sjulian hook->flags = HK_INVALID; 69452419Sjulian hook->node = node; 69552419Sjulian node->refs++; /* each hook counts as a reference */ 69652419Sjulian 69752419Sjulian /* Check if the node type code has something to say about it */ 69852419Sjulian if (node->type->newhook != NULL) 69952419Sjulian if ((error = (*node->type->newhook)(node, hook, name)) != 0) 70052419Sjulian goto fail; 70152419Sjulian 70252419Sjulian /* 70352419Sjulian * The 'type' agrees so far, so go ahead and link it in. 70452419Sjulian * We'll ask again later when we actually connect the hooks. 70552419Sjulian */ 70652419Sjulian LIST_INSERT_HEAD(&node->hooks, hook, hooks); 70752419Sjulian node->numhooks++; 70852419Sjulian 70952419Sjulian /* Set hook name */ 71063675Sarchie MALLOC(hook->name, char *, strlen(name) + 1, M_NETGRAPH, M_NOWAIT); 71152419Sjulian if (hook->name == NULL) { 71252419Sjulian error = ENOMEM; 71352419Sjulian LIST_REMOVE(hook, hooks); 71452419Sjulian node->numhooks--; 71552419Sjulianfail: 71652419Sjulian hook->node = NULL; 71752419Sjulian ng_unref(node); 71852419Sjulian ng_unref_hook(hook); /* this frees the hook */ 71952419Sjulian return (error); 72052419Sjulian } 72152419Sjulian strcpy(hook->name, name); 72252419Sjulian if (hookp) 72352419Sjulian *hookp = hook; 72452419Sjulian return (error); 72552419Sjulian} 72652419Sjulian 72752419Sjulian/* 72852419Sjulian * Connect a pair of hooks. Only used internally. 72952419Sjulian */ 73052419Sjulianstatic int 73152419Sjulianng_connect(hook_p hook1, hook_p hook2) 73252419Sjulian{ 73352419Sjulian int error; 73452419Sjulian 73552419Sjulian hook1->peer = hook2; 73652419Sjulian hook2->peer = hook1; 73752419Sjulian 73852419Sjulian /* Give each node the opportunity to veto the impending connection */ 73952419Sjulian if (hook1->node->type->connect) { 74052419Sjulian if ((error = (*hook1->node->type->connect) (hook1))) { 74152419Sjulian ng_destroy_hook(hook1); /* also zaps hook2 */ 74252419Sjulian return (error); 74352419Sjulian } 74452419Sjulian } 74552419Sjulian if (hook2->node->type->connect) { 74652419Sjulian if ((error = (*hook2->node->type->connect) (hook2))) { 74752419Sjulian ng_destroy_hook(hook2); /* also zaps hook1 */ 74852419Sjulian return (error); 74952419Sjulian } 75052419Sjulian } 75152419Sjulian hook1->flags &= ~HK_INVALID; 75252419Sjulian hook2->flags &= ~HK_INVALID; 75352419Sjulian return (0); 75452419Sjulian} 75552419Sjulian 75652419Sjulian/* 75754096Sarchie * Find a hook 75854096Sarchie * 75954096Sarchie * Node types may supply their own optimized routines for finding 76054096Sarchie * hooks. If none is supplied, we just do a linear search. 76154096Sarchie */ 76254096Sarchiehook_p 76354096Sarchieng_findhook(node_p node, const char *name) 76454096Sarchie{ 76554096Sarchie hook_p hook; 76654096Sarchie 76754096Sarchie if (node->type->findhook != NULL) 76854096Sarchie return (*node->type->findhook)(node, name); 76954096Sarchie LIST_FOREACH(hook, &node->hooks, hooks) { 77054096Sarchie if (hook->name != NULL && strcmp(hook->name, name) == 0) 77154096Sarchie return (hook); 77254096Sarchie } 77354096Sarchie return (NULL); 77454096Sarchie} 77554096Sarchie 77654096Sarchie/* 77752419Sjulian * Destroy a hook 77852419Sjulian * 77952419Sjulian * As hooks are always attached, this really destroys two hooks. 78052419Sjulian * The one given, and the one attached to it. Disconnect the hooks 78152419Sjulian * from each other first. 78252419Sjulian */ 78352419Sjulianvoid 78452419Sjulianng_destroy_hook(hook_p hook) 78552419Sjulian{ 78652419Sjulian hook_p peer = hook->peer; 78752419Sjulian 78852419Sjulian hook->flags |= HK_INVALID; /* as soon as possible */ 78952419Sjulian if (peer) { 79052419Sjulian peer->flags |= HK_INVALID; /* as soon as possible */ 79152419Sjulian hook->peer = NULL; 79252419Sjulian peer->peer = NULL; 79352419Sjulian ng_disconnect_hook(peer); 79452419Sjulian } 79552419Sjulian ng_disconnect_hook(hook); 79652419Sjulian} 79752419Sjulian 79852419Sjulian/* 79952419Sjulian * Notify the node of the hook's demise. This may result in more actions 80052419Sjulian * (e.g. shutdown) but we don't do that ourselves and don't know what 80152419Sjulian * happens there. If there is no appropriate handler, then just remove it 80252419Sjulian * (and decrement the reference count of it's node which in turn might 80352419Sjulian * make something happen). 80452419Sjulian */ 80552419Sjulianstatic void 80652419Sjulianng_disconnect_hook(hook_p hook) 80752419Sjulian{ 80852419Sjulian node_p node = hook->node; 80952419Sjulian 81052419Sjulian /* 81152419Sjulian * Remove the hook from the node's list to avoid possible recursion 81252419Sjulian * in case the disconnection results in node shutdown. 81352419Sjulian */ 81452419Sjulian LIST_REMOVE(hook, hooks); 81552419Sjulian node->numhooks--; 81652419Sjulian if (node->type->disconnect) { 81752419Sjulian /* 81852419Sjulian * The type handler may elect to destroy the peer so don't 81952419Sjulian * trust its existance after this point. 82052419Sjulian */ 82152419Sjulian (*node->type->disconnect) (hook); 82252419Sjulian } 82352419Sjulian ng_unref(node); /* might be the last reference */ 82452419Sjulian if (hook->name) 82552419Sjulian FREE(hook->name, M_NETGRAPH); 82652419Sjulian hook->node = NULL; /* may still be referenced elsewhere */ 82752419Sjulian ng_unref_hook(hook); 82852419Sjulian} 82952419Sjulian 83052419Sjulian/* 83152419Sjulian * Take two hooks on a node and merge the connection so that the given node 83252419Sjulian * is effectively bypassed. 83352419Sjulian */ 83452419Sjulianint 83552419Sjulianng_bypass(hook_p hook1, hook_p hook2) 83652419Sjulian{ 83752419Sjulian if (hook1->node != hook2->node) 83852419Sjulian return (EINVAL); 83952419Sjulian hook1->peer->peer = hook2->peer; 84052419Sjulian hook2->peer->peer = hook1->peer; 84152419Sjulian 84252419Sjulian /* XXX If we ever cache methods on hooks update them as well */ 84352419Sjulian hook1->peer = NULL; 84452419Sjulian hook2->peer = NULL; 84552419Sjulian ng_destroy_hook(hook1); 84652419Sjulian ng_destroy_hook(hook2); 84752419Sjulian return (0); 84852419Sjulian} 84952419Sjulian 85052419Sjulian/* 85152419Sjulian * Install a new netgraph type 85252419Sjulian */ 85352419Sjulianint 85452419Sjulianng_newtype(struct ng_type *tp) 85552419Sjulian{ 85652419Sjulian const size_t namelen = strlen(tp->name); 85752419Sjulian 85852419Sjulian /* Check version and type name fields */ 85952419Sjulian if (tp->version != NG_VERSION || namelen == 0 || namelen > NG_TYPELEN) { 86052419Sjulian TRAP_ERROR; 86152419Sjulian return (EINVAL); 86252419Sjulian } 86352419Sjulian 86452419Sjulian /* Check for name collision */ 86552419Sjulian if (ng_findtype(tp->name) != NULL) { 86652419Sjulian TRAP_ERROR; 86752419Sjulian return (EEXIST); 86852419Sjulian } 86952419Sjulian 87052419Sjulian /* Link in new type */ 87152419Sjulian LIST_INSERT_HEAD(&typelist, tp, types); 87252419Sjulian tp->refs = 0; 87352419Sjulian return (0); 87452419Sjulian} 87552419Sjulian 87652419Sjulian/* 87752419Sjulian * Look for a type of the name given 87852419Sjulian */ 87952419Sjulianstruct ng_type * 88052419Sjulianng_findtype(const char *typename) 88152419Sjulian{ 88252419Sjulian struct ng_type *type; 88352419Sjulian 88452419Sjulian LIST_FOREACH(type, &typelist, types) { 88552419Sjulian if (strcmp(type->name, typename) == 0) 88652419Sjulian break; 88752419Sjulian } 88852419Sjulian return (type); 88952419Sjulian} 89052419Sjulian 89152419Sjulian 89252419Sjulian/************************************************************************ 89352419Sjulian Composite routines 89452419Sjulian************************************************************************/ 89552419Sjulian 89652419Sjulian/* 89752419Sjulian * Make a peer and connect. The order is arranged to minimise 89852419Sjulian * the work needed to back out in case of error. 89952419Sjulian */ 90052419Sjulianint 90152419Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type) 90252419Sjulian{ 90352419Sjulian node_p node2; 90452419Sjulian hook_p hook; 90552419Sjulian hook_p hook2; 90652419Sjulian int error; 90752419Sjulian 90852419Sjulian if ((error = ng_add_hook(node, name, &hook))) 90952419Sjulian return (error); 91052419Sjulian if ((error = ng_make_node(type, &node2))) { 91152419Sjulian ng_destroy_hook(hook); 91252419Sjulian return (error); 91352419Sjulian } 91452419Sjulian if ((error = ng_add_hook(node2, name2, &hook2))) { 91552419Sjulian ng_rmnode(node2); 91652419Sjulian ng_destroy_hook(hook); 91752419Sjulian return (error); 91852419Sjulian } 91952419Sjulian 92052419Sjulian /* 92152419Sjulian * Actually link the two hooks together.. on failure they are 92252419Sjulian * destroyed so we don't have to do that here. 92352419Sjulian */ 92452419Sjulian if ((error = ng_connect(hook, hook2))) 92552419Sjulian ng_rmnode(node2); 92652419Sjulian return (error); 92752419Sjulian} 92852419Sjulian 92952419Sjulian/* 93052419Sjulian * Connect two nodes using the specified hooks 93152419Sjulian */ 93252419Sjulianint 93352419Sjulianng_con_nodes(node_p node, const char *name, node_p node2, const char *name2) 93452419Sjulian{ 93552419Sjulian int error; 93652419Sjulian hook_p hook; 93752419Sjulian hook_p hook2; 93852419Sjulian 93952419Sjulian if ((error = ng_add_hook(node, name, &hook))) 94052419Sjulian return (error); 94152419Sjulian if ((error = ng_add_hook(node2, name2, &hook2))) { 94252419Sjulian ng_destroy_hook(hook); 94352419Sjulian return (error); 94452419Sjulian } 94552419Sjulian return (ng_connect(hook, hook2)); 94652419Sjulian} 94752419Sjulian 94852419Sjulian/* 94952419Sjulian * Parse and verify a string of the form: <NODE:><PATH> 95052419Sjulian * 95152419Sjulian * Such a string can refer to a specific node or a specific hook 95252419Sjulian * on a specific node, depending on how you look at it. In the 95352419Sjulian * latter case, the PATH component must not end in a dot. 95452419Sjulian * 95552419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string 95652419Sjulian * of hook names separated by dots. This breaks out the original 95752419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp 95852419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to 95952419Sjulian * the final hook component of <PATH>, if any, otherwise NULL. 96052419Sjulian * 96152419Sjulian * This returns -1 if the path is malformed. The char ** are optional. 96252419Sjulian */ 96352419Sjulian 96452419Sjulianint 96552419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 96652419Sjulian{ 96752419Sjulian char *node, *path, *hook; 96852419Sjulian int k; 96952419Sjulian 97052419Sjulian /* 97152419Sjulian * Extract absolute NODE, if any 97252419Sjulian */ 97352419Sjulian for (path = addr; *path && *path != ':'; path++); 97452419Sjulian if (*path) { 97552419Sjulian node = addr; /* Here's the NODE */ 97652419Sjulian *path++ = '\0'; /* Here's the PATH */ 97752419Sjulian 97852419Sjulian /* Node name must not be empty */ 97952419Sjulian if (!*node) 98052419Sjulian return -1; 98152419Sjulian 98252419Sjulian /* A name of "." is OK; otherwise '.' not allowed */ 98352419Sjulian if (strcmp(node, ".") != 0) { 98452419Sjulian for (k = 0; node[k]; k++) 98552419Sjulian if (node[k] == '.') 98652419Sjulian return -1; 98752419Sjulian } 98852419Sjulian } else { 98952419Sjulian node = NULL; /* No absolute NODE */ 99052419Sjulian path = addr; /* Here's the PATH */ 99152419Sjulian } 99252419Sjulian 99352419Sjulian /* Snoop for illegal characters in PATH */ 99452419Sjulian for (k = 0; path[k]; k++) 99552419Sjulian if (path[k] == ':') 99652419Sjulian return -1; 99752419Sjulian 99852419Sjulian /* Check for no repeated dots in PATH */ 99952419Sjulian for (k = 0; path[k]; k++) 100052419Sjulian if (path[k] == '.' && path[k + 1] == '.') 100152419Sjulian return -1; 100252419Sjulian 100352419Sjulian /* Remove extra (degenerate) dots from beginning or end of PATH */ 100452419Sjulian if (path[0] == '.') 100552419Sjulian path++; 100652419Sjulian if (*path && path[strlen(path) - 1] == '.') 100752419Sjulian path[strlen(path) - 1] = 0; 100852419Sjulian 100952419Sjulian /* If PATH has a dot, then we're not talking about a hook */ 101052419Sjulian if (*path) { 101152419Sjulian for (hook = path, k = 0; path[k]; k++) 101252419Sjulian if (path[k] == '.') { 101352419Sjulian hook = NULL; 101452419Sjulian break; 101552419Sjulian } 101652419Sjulian } else 101752419Sjulian path = hook = NULL; 101852419Sjulian 101952419Sjulian /* Done */ 102052419Sjulian if (nodep) 102152419Sjulian *nodep = node; 102252419Sjulian if (pathp) 102352419Sjulian *pathp = path; 102452419Sjulian if (hookp) 102552419Sjulian *hookp = hook; 102652419Sjulian return (0); 102752419Sjulian} 102852419Sjulian 102952419Sjulian/* 103052419Sjulian * Given a path, which may be absolute or relative, and a starting node, 103152419Sjulian * return the destination node. Compute the "return address" if desired. 103252419Sjulian */ 103352419Sjulianint 103469922Sjulianng_path2node(node_p here, const char *address, node_p *destp, hook_p *lasthook) 103552419Sjulian{ 103652419Sjulian char fullpath[NG_PATHLEN + 1]; 103752419Sjulian char *nodename, *path, pbuf[2]; 103852419Sjulian node_p node; 103952419Sjulian char *cp; 104059728Sjulian hook_p hook = NULL; 104152419Sjulian 104252419Sjulian /* Initialize */ 104352419Sjulian if (destp == NULL) 104452419Sjulian return EINVAL; 104552419Sjulian *destp = NULL; 104652419Sjulian 104752419Sjulian /* Make a writable copy of address for ng_path_parse() */ 104852419Sjulian strncpy(fullpath, address, sizeof(fullpath) - 1); 104952419Sjulian fullpath[sizeof(fullpath) - 1] = '\0'; 105052419Sjulian 105152419Sjulian /* Parse out node and sequence of hooks */ 105252419Sjulian if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 105352419Sjulian TRAP_ERROR; 105452419Sjulian return EINVAL; 105552419Sjulian } 105652419Sjulian if (path == NULL) { 105752419Sjulian pbuf[0] = '.'; /* Needs to be writable */ 105852419Sjulian pbuf[1] = '\0'; 105952419Sjulian path = pbuf; 106052419Sjulian } 106152419Sjulian 106252419Sjulian /* For an absolute address, jump to the starting node */ 106352419Sjulian if (nodename) { 106452419Sjulian node = ng_findname(here, nodename); 106552419Sjulian if (node == NULL) { 106652419Sjulian TRAP_ERROR; 106752419Sjulian return (ENOENT); 106852419Sjulian } 106952419Sjulian } else 107052419Sjulian node = here; 107152419Sjulian 107252419Sjulian /* Now follow the sequence of hooks */ 107352419Sjulian for (cp = path; node != NULL && *cp != '\0'; ) { 107452419Sjulian char *segment; 107552419Sjulian 107652419Sjulian /* 107752419Sjulian * Break out the next path segment. Replace the dot we just 107852419Sjulian * found with a NUL; "cp" points to the next segment (or the 107952419Sjulian * NUL at the end). 108052419Sjulian */ 108152419Sjulian for (segment = cp; *cp != '\0'; cp++) { 108252419Sjulian if (*cp == '.') { 108352419Sjulian *cp++ = '\0'; 108452419Sjulian break; 108552419Sjulian } 108652419Sjulian } 108752419Sjulian 108852419Sjulian /* Empty segment */ 108952419Sjulian if (*segment == '\0') 109052419Sjulian continue; 109152419Sjulian 109252419Sjulian /* We have a segment, so look for a hook by that name */ 109354096Sarchie hook = ng_findhook(node, segment); 109452419Sjulian 109552419Sjulian /* Can't get there from here... */ 109652419Sjulian if (hook == NULL 109752419Sjulian || hook->peer == NULL 109852419Sjulian || (hook->flags & HK_INVALID) != 0) { 109952419Sjulian TRAP_ERROR; 110052419Sjulian return (ENOENT); 110152419Sjulian } 110252419Sjulian 110352419Sjulian /* Hop on over to the next node */ 110452419Sjulian node = hook->peer->node; 110552419Sjulian } 110652419Sjulian 110752419Sjulian /* If node somehow missing, fail here (probably this is not needed) */ 110852419Sjulian if (node == NULL) { 110952419Sjulian TRAP_ERROR; 111052419Sjulian return (ENXIO); 111152419Sjulian } 111252419Sjulian 111352419Sjulian /* Done */ 111452419Sjulian *destp = node; 111559900Sarchie if (lasthook != NULL) 111659900Sarchie *lasthook = hook ? hook->peer : NULL; 111752419Sjulian return (0); 111852419Sjulian} 111952419Sjulian 112052419Sjulian/* 112152419Sjulian * Call the appropriate message handler for the object. 112252419Sjulian * It is up to the message handler to free the message. 112352419Sjulian * If it's a generic message, handle it generically, otherwise 112452419Sjulian * call the type's message handler (if it exists) 112569519Sjulian * XXX (race). Remember that a queued message may reference a node 112669519Sjulian * or hook that has just been invalidated. It will exist 112769519Sjulian * as the queue code is holding a reference, but.. 112852419Sjulian */ 112952419Sjulian 113059728Sjulian#define CALL_MSG_HANDLER(error, node, msg, retaddr, resp, hook) \ 113152419Sjuliando { \ 113252419Sjulian if((msg)->header.typecookie == NGM_GENERIC_COOKIE) { \ 113352419Sjulian (error) = ng_generic_msg((node), (msg), \ 113459728Sjulian (retaddr), (resp), (hook)); \ 113552419Sjulian } else { \ 113652419Sjulian if ((node)->type->rcvmsg != NULL) { \ 113752419Sjulian (error) = (*(node)->type->rcvmsg)((node), \ 113859728Sjulian (msg), (retaddr), (resp), (hook)); \ 113952419Sjulian } else { \ 114052419Sjulian TRAP_ERROR; \ 114152419Sjulian FREE((msg), M_NETGRAPH); \ 114252419Sjulian (error) = EINVAL; \ 114352419Sjulian } \ 114452419Sjulian } \ 114552419Sjulian} while (0) 114652419Sjulian 114752419Sjulian 114852419Sjulian/* 114969922Sjulian * Send a control message to a node. 115069922Sjulian * If hook is supplied, use it in preference to the address. 115169922Sjulian * If the return address is not supplied it will be set to this node. 115252419Sjulian */ 115352419Sjulianint 115452419Sjulianng_send_msg(node_p here, struct ng_mesg *msg, const char *address, 115569922Sjulian hook_p hook, char *retaddr, struct ng_mesg **rptr) 115652419Sjulian{ 115752419Sjulian node_p dest = NULL; 115852419Sjulian int error; 115959728Sjulian hook_p lasthook; 116052419Sjulian 116169922Sjulian /* 116269922Sjulian * Find the target node. 116369922Sjulian * If there is a HOOK argument, then use that in preference 116469922Sjulian * to the address. 116569922Sjulian */ 116669922Sjulian if (hook) { 116769922Sjulian lasthook = hook->peer; 116869922Sjulian dest = lasthook->node; 116969922Sjulian } else { 117069922Sjulian error = ng_path2node(here, address, &dest, &lasthook); 117169922Sjulian if (error) { 117269922Sjulian FREE(msg, M_NETGRAPH); 117369922Sjulian return (error); 117469922Sjulian } 117552419Sjulian } 117652419Sjulian 117769922Sjulian /* If the user didn't supply a return addres, assume it's "here". */ 117869922Sjulian if (retaddr == NULL) { 117969922Sjulian /* 118069922Sjulian * Now fill out the return address, 118169922Sjulian * i.e. the name/ID of the sender. (If we didn't get one) 118269922Sjulian */ 118369922Sjulian MALLOC(retaddr, char *, NG_NODELEN + 2, M_NETGRAPH, M_NOWAIT); 118469922Sjulian if (retaddr == NULL) { 118569922Sjulian TRAP_ERROR; 118669922Sjulian return (ENOMEM); 118769922Sjulian } 118869922Sjulian if (here->name != NULL) 118969922Sjulian sprintf(retaddr, "%s:", here->name); 119069922Sjulian else 119169922Sjulian sprintf(retaddr, "[%x]:", ng_node2ID(here)); 119269922Sjulian } 119369922Sjulian 119452419Sjulian /* Make sure the resp field is null before we start */ 119552419Sjulian if (rptr != NULL) 119652419Sjulian *rptr = NULL; 119752419Sjulian 119859728Sjulian CALL_MSG_HANDLER(error, dest, msg, retaddr, rptr, lasthook); 119952419Sjulian 120052419Sjulian /* Make sure that if there is a response, it has the RESP bit set */ 120152419Sjulian if ((error == 0) && rptr && *rptr) 120252419Sjulian (*rptr)->header.flags |= NGF_RESP; 120352419Sjulian 120452419Sjulian /* 120552419Sjulian * If we had a return address it is up to us to free it. They should 120652419Sjulian * have taken a copy if they needed to make a delayed response. 120752419Sjulian */ 120852419Sjulian if (retaddr) 120952419Sjulian FREE(retaddr, M_NETGRAPH); 121052419Sjulian return (error); 121152419Sjulian} 121252419Sjulian 121352419Sjulian/* 121452419Sjulian * Implement the 'generic' control messages 121552419Sjulian */ 121652419Sjulianstatic int 121752419Sjulianng_generic_msg(node_p here, struct ng_mesg *msg, const char *retaddr, 121859728Sjulian struct ng_mesg **resp, hook_p lasthook) 121952419Sjulian{ 122052419Sjulian int error = 0; 122152419Sjulian 122252419Sjulian if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 122352419Sjulian TRAP_ERROR; 122452419Sjulian FREE(msg, M_NETGRAPH); 122552419Sjulian return (EINVAL); 122652419Sjulian } 122752419Sjulian switch (msg->header.cmd) { 122852419Sjulian case NGM_SHUTDOWN: 122952419Sjulian ng_rmnode(here); 123052419Sjulian break; 123152419Sjulian case NGM_MKPEER: 123252419Sjulian { 123352419Sjulian struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 123452419Sjulian 123552419Sjulian if (msg->header.arglen != sizeof(*mkp)) { 123652419Sjulian TRAP_ERROR; 123752419Sjulian return (EINVAL); 123852419Sjulian } 123952419Sjulian mkp->type[sizeof(mkp->type) - 1] = '\0'; 124052419Sjulian mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 124152419Sjulian mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 124252419Sjulian error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 124352419Sjulian break; 124452419Sjulian } 124552419Sjulian case NGM_CONNECT: 124652419Sjulian { 124752419Sjulian struct ngm_connect *const con = 124852419Sjulian (struct ngm_connect *) msg->data; 124952419Sjulian node_p node2; 125052419Sjulian 125152419Sjulian if (msg->header.arglen != sizeof(*con)) { 125252419Sjulian TRAP_ERROR; 125352419Sjulian return (EINVAL); 125452419Sjulian } 125552419Sjulian con->path[sizeof(con->path) - 1] = '\0'; 125652419Sjulian con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 125752419Sjulian con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 125869922Sjulian error = ng_path2node(here, con->path, &node2, NULL); 125952419Sjulian if (error) 126052419Sjulian break; 126152419Sjulian error = ng_con_nodes(here, con->ourhook, node2, con->peerhook); 126252419Sjulian break; 126352419Sjulian } 126452419Sjulian case NGM_NAME: 126552419Sjulian { 126652419Sjulian struct ngm_name *const nam = (struct ngm_name *) msg->data; 126752419Sjulian 126852419Sjulian if (msg->header.arglen != sizeof(*nam)) { 126952419Sjulian TRAP_ERROR; 127052419Sjulian return (EINVAL); 127152419Sjulian } 127252419Sjulian nam->name[sizeof(nam->name) - 1] = '\0'; 127352419Sjulian error = ng_name_node(here, nam->name); 127452419Sjulian break; 127552419Sjulian } 127652419Sjulian case NGM_RMHOOK: 127752419Sjulian { 127852419Sjulian struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 127952419Sjulian hook_p hook; 128052419Sjulian 128152419Sjulian if (msg->header.arglen != sizeof(*rmh)) { 128252419Sjulian TRAP_ERROR; 128352419Sjulian return (EINVAL); 128452419Sjulian } 128552419Sjulian rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 128654096Sarchie if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 128752419Sjulian ng_destroy_hook(hook); 128852419Sjulian break; 128952419Sjulian } 129052419Sjulian case NGM_NODEINFO: 129152419Sjulian { 129252419Sjulian struct nodeinfo *ni; 129352419Sjulian struct ng_mesg *rp; 129452419Sjulian 129552419Sjulian /* Get response struct */ 129652419Sjulian if (resp == NULL) { 129752419Sjulian error = EINVAL; 129852419Sjulian break; 129952419Sjulian } 130052419Sjulian NG_MKRESPONSE(rp, msg, sizeof(*ni), M_NOWAIT); 130152419Sjulian if (rp == NULL) { 130252419Sjulian error = ENOMEM; 130352419Sjulian break; 130452419Sjulian } 130552419Sjulian 130652419Sjulian /* Fill in node info */ 130752419Sjulian ni = (struct nodeinfo *) rp->data; 130852419Sjulian if (here->name != NULL) 130952419Sjulian strncpy(ni->name, here->name, NG_NODELEN); 131052419Sjulian strncpy(ni->type, here->type->name, NG_TYPELEN); 131152722Sjulian ni->id = ng_node2ID(here); 131252419Sjulian ni->hooks = here->numhooks; 131352419Sjulian *resp = rp; 131452419Sjulian break; 131552419Sjulian } 131652419Sjulian case NGM_LISTHOOKS: 131752419Sjulian { 131852419Sjulian const int nhooks = here->numhooks; 131952419Sjulian struct hooklist *hl; 132052419Sjulian struct nodeinfo *ni; 132152419Sjulian struct ng_mesg *rp; 132252419Sjulian hook_p hook; 132352419Sjulian 132452419Sjulian /* Get response struct */ 132552419Sjulian if (resp == NULL) { 132652419Sjulian error = EINVAL; 132752419Sjulian break; 132852419Sjulian } 132952419Sjulian NG_MKRESPONSE(rp, msg, sizeof(*hl) 133052419Sjulian + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 133152419Sjulian if (rp == NULL) { 133252419Sjulian error = ENOMEM; 133352419Sjulian break; 133452419Sjulian } 133552419Sjulian hl = (struct hooklist *) rp->data; 133652419Sjulian ni = &hl->nodeinfo; 133752419Sjulian 133852419Sjulian /* Fill in node info */ 133952419Sjulian if (here->name) 134052419Sjulian strncpy(ni->name, here->name, NG_NODELEN); 134152419Sjulian strncpy(ni->type, here->type->name, NG_TYPELEN); 134252722Sjulian ni->id = ng_node2ID(here); 134352419Sjulian 134452419Sjulian /* Cycle through the linked list of hooks */ 134552419Sjulian ni->hooks = 0; 134652419Sjulian LIST_FOREACH(hook, &here->hooks, hooks) { 134752419Sjulian struct linkinfo *const link = &hl->link[ni->hooks]; 134852419Sjulian 134952419Sjulian if (ni->hooks >= nhooks) { 135052419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 135152419Sjulian __FUNCTION__, "hooks"); 135252419Sjulian break; 135352419Sjulian } 135452419Sjulian if ((hook->flags & HK_INVALID) != 0) 135552419Sjulian continue; 135652419Sjulian strncpy(link->ourhook, hook->name, NG_HOOKLEN); 135752419Sjulian strncpy(link->peerhook, hook->peer->name, NG_HOOKLEN); 135852419Sjulian if (hook->peer->node->name != NULL) 135952419Sjulian strncpy(link->nodeinfo.name, 136052419Sjulian hook->peer->node->name, NG_NODELEN); 136152419Sjulian strncpy(link->nodeinfo.type, 136252419Sjulian hook->peer->node->type->name, NG_TYPELEN); 136352722Sjulian link->nodeinfo.id = ng_node2ID(hook->peer->node); 136452419Sjulian link->nodeinfo.hooks = hook->peer->node->numhooks; 136552419Sjulian ni->hooks++; 136652419Sjulian } 136752419Sjulian *resp = rp; 136852419Sjulian break; 136952419Sjulian } 137052419Sjulian 137152419Sjulian case NGM_LISTNAMES: 137252419Sjulian case NGM_LISTNODES: 137352419Sjulian { 137452419Sjulian const int unnamed = (msg->header.cmd == NGM_LISTNODES); 137552419Sjulian struct namelist *nl; 137652419Sjulian struct ng_mesg *rp; 137752419Sjulian node_p node; 137852419Sjulian int num = 0; 137952419Sjulian 138052419Sjulian if (resp == NULL) { 138152419Sjulian error = EINVAL; 138252419Sjulian break; 138352419Sjulian } 138452419Sjulian 138552419Sjulian /* Count number of nodes */ 138652419Sjulian LIST_FOREACH(node, &nodelist, nodes) { 138752419Sjulian if (unnamed || node->name != NULL) 138852419Sjulian num++; 138952419Sjulian } 139052419Sjulian 139152419Sjulian /* Get response struct */ 139252419Sjulian if (resp == NULL) { 139352419Sjulian error = EINVAL; 139452419Sjulian break; 139552419Sjulian } 139652419Sjulian NG_MKRESPONSE(rp, msg, sizeof(*nl) 139752419Sjulian + (num * sizeof(struct nodeinfo)), M_NOWAIT); 139852419Sjulian if (rp == NULL) { 139952419Sjulian error = ENOMEM; 140052419Sjulian break; 140152419Sjulian } 140252419Sjulian nl = (struct namelist *) rp->data; 140352419Sjulian 140452419Sjulian /* Cycle through the linked list of nodes */ 140552419Sjulian nl->numnames = 0; 140652419Sjulian LIST_FOREACH(node, &nodelist, nodes) { 140752419Sjulian struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; 140852419Sjulian 140952419Sjulian if (nl->numnames >= num) { 141052419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 141152419Sjulian __FUNCTION__, "nodes"); 141252419Sjulian break; 141352419Sjulian } 141452419Sjulian if ((node->flags & NG_INVALID) != 0) 141552419Sjulian continue; 141652419Sjulian if (!unnamed && node->name == NULL) 141752419Sjulian continue; 141852419Sjulian if (node->name != NULL) 141952419Sjulian strncpy(np->name, node->name, NG_NODELEN); 142052419Sjulian strncpy(np->type, node->type->name, NG_TYPELEN); 142152722Sjulian np->id = ng_node2ID(node); 142252419Sjulian np->hooks = node->numhooks; 142352419Sjulian nl->numnames++; 142452419Sjulian } 142552419Sjulian *resp = rp; 142652419Sjulian break; 142752419Sjulian } 142852419Sjulian 142952419Sjulian case NGM_LISTTYPES: 143052419Sjulian { 143152419Sjulian struct typelist *tl; 143252419Sjulian struct ng_mesg *rp; 143352419Sjulian struct ng_type *type; 143452419Sjulian int num = 0; 143552419Sjulian 143652419Sjulian if (resp == NULL) { 143752419Sjulian error = EINVAL; 143852419Sjulian break; 143952419Sjulian } 144052419Sjulian 144152419Sjulian /* Count number of types */ 144252419Sjulian LIST_FOREACH(type, &typelist, types) 144352419Sjulian num++; 144452419Sjulian 144552419Sjulian /* Get response struct */ 144652419Sjulian if (resp == NULL) { 144752419Sjulian error = EINVAL; 144852419Sjulian break; 144952419Sjulian } 145052419Sjulian NG_MKRESPONSE(rp, msg, sizeof(*tl) 145152419Sjulian + (num * sizeof(struct typeinfo)), M_NOWAIT); 145252419Sjulian if (rp == NULL) { 145352419Sjulian error = ENOMEM; 145452419Sjulian break; 145552419Sjulian } 145652419Sjulian tl = (struct typelist *) rp->data; 145752419Sjulian 145852419Sjulian /* Cycle through the linked list of types */ 145952419Sjulian tl->numtypes = 0; 146052419Sjulian LIST_FOREACH(type, &typelist, types) { 146152419Sjulian struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 146252419Sjulian 146352419Sjulian if (tl->numtypes >= num) { 146452419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 146552419Sjulian __FUNCTION__, "types"); 146652419Sjulian break; 146752419Sjulian } 146859879Sarchie strncpy(tp->type_name, type->name, NG_TYPELEN); 146952419Sjulian tp->numnodes = type->refs; 147052419Sjulian tl->numtypes++; 147152419Sjulian } 147252419Sjulian *resp = rp; 147352419Sjulian break; 147452419Sjulian } 147552419Sjulian 147653913Sarchie case NGM_BINARY2ASCII: 147753913Sarchie { 147864510Sarchie int bufSize = 20 * 1024; /* XXX hard coded constant */ 147953913Sarchie const struct ng_parse_type *argstype; 148053913Sarchie const struct ng_cmdlist *c; 148153913Sarchie struct ng_mesg *rp, *binary, *ascii; 148253913Sarchie 148353913Sarchie /* Data area must contain a valid netgraph message */ 148453913Sarchie binary = (struct ng_mesg *)msg->data; 148553913Sarchie if (msg->header.arglen < sizeof(struct ng_mesg) 148653913Sarchie || msg->header.arglen - sizeof(struct ng_mesg) 148753913Sarchie < binary->header.arglen) { 148853913Sarchie error = EINVAL; 148953913Sarchie break; 149053913Sarchie } 149153913Sarchie 149253913Sarchie /* Get a response message with lots of room */ 149353913Sarchie NG_MKRESPONSE(rp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 149453913Sarchie if (rp == NULL) { 149553913Sarchie error = ENOMEM; 149653913Sarchie break; 149753913Sarchie } 149853913Sarchie ascii = (struct ng_mesg *)rp->data; 149953913Sarchie 150053913Sarchie /* Copy binary message header to response message payload */ 150153913Sarchie bcopy(binary, ascii, sizeof(*binary)); 150253913Sarchie 150353913Sarchie /* Find command by matching typecookie and command number */ 150453913Sarchie for (c = here->type->cmdlist; 150553913Sarchie c != NULL && c->name != NULL; c++) { 150653913Sarchie if (binary->header.typecookie == c->cookie 150753913Sarchie && binary->header.cmd == c->cmd) 150853913Sarchie break; 150953913Sarchie } 151053913Sarchie if (c == NULL || c->name == NULL) { 151153913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 151253913Sarchie if (binary->header.typecookie == c->cookie 151353913Sarchie && binary->header.cmd == c->cmd) 151453913Sarchie break; 151553913Sarchie } 151653913Sarchie if (c->name == NULL) { 151753913Sarchie FREE(rp, M_NETGRAPH); 151853913Sarchie error = ENOSYS; 151953913Sarchie break; 152053913Sarchie } 152153913Sarchie } 152253913Sarchie 152353913Sarchie /* Convert command name to ASCII */ 152453913Sarchie snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 152553913Sarchie "%s", c->name); 152653913Sarchie 152753913Sarchie /* Convert command arguments to ASCII */ 152853913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 152953913Sarchie c->respType : c->mesgType; 153053913Sarchie if (argstype == NULL) 153153913Sarchie *ascii->data = '\0'; 153253913Sarchie else { 153353913Sarchie if ((error = ng_unparse(argstype, 153453913Sarchie (u_char *)binary->data, 153553913Sarchie ascii->data, bufSize)) != 0) { 153653913Sarchie FREE(rp, M_NETGRAPH); 153753913Sarchie break; 153853913Sarchie } 153953913Sarchie } 154053913Sarchie 154153913Sarchie /* Return the result as struct ng_mesg plus ASCII string */ 154253913Sarchie bufSize = strlen(ascii->data) + 1; 154353913Sarchie ascii->header.arglen = bufSize; 154453913Sarchie rp->header.arglen = sizeof(*ascii) + bufSize; 154553913Sarchie *resp = rp; 154653913Sarchie break; 154753913Sarchie } 154853913Sarchie 154953913Sarchie case NGM_ASCII2BINARY: 155053913Sarchie { 155153913Sarchie int bufSize = 2000; /* XXX hard coded constant */ 155253913Sarchie const struct ng_cmdlist *c; 155353913Sarchie const struct ng_parse_type *argstype; 155453913Sarchie struct ng_mesg *rp, *ascii, *binary; 155559178Sarchie int off = 0; 155653913Sarchie 155753913Sarchie /* Data area must contain at least a struct ng_mesg + '\0' */ 155853913Sarchie ascii = (struct ng_mesg *)msg->data; 155953913Sarchie if (msg->header.arglen < sizeof(*ascii) + 1 156053913Sarchie || ascii->header.arglen < 1 156153913Sarchie || msg->header.arglen 156253913Sarchie < sizeof(*ascii) + ascii->header.arglen) { 156353913Sarchie error = EINVAL; 156453913Sarchie break; 156553913Sarchie } 156653913Sarchie ascii->data[ascii->header.arglen - 1] = '\0'; 156753913Sarchie 156853913Sarchie /* Get a response message with lots of room */ 156953913Sarchie NG_MKRESPONSE(rp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 157053913Sarchie if (rp == NULL) { 157153913Sarchie error = ENOMEM; 157253913Sarchie break; 157353913Sarchie } 157453913Sarchie binary = (struct ng_mesg *)rp->data; 157553913Sarchie 157653913Sarchie /* Copy ASCII message header to response message payload */ 157753913Sarchie bcopy(ascii, binary, sizeof(*ascii)); 157853913Sarchie 157953913Sarchie /* Find command by matching ASCII command string */ 158053913Sarchie for (c = here->type->cmdlist; 158153913Sarchie c != NULL && c->name != NULL; c++) { 158253913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 158353913Sarchie break; 158453913Sarchie } 158553913Sarchie if (c == NULL || c->name == NULL) { 158653913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 158753913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 158853913Sarchie break; 158953913Sarchie } 159053913Sarchie if (c->name == NULL) { 159153913Sarchie FREE(rp, M_NETGRAPH); 159253913Sarchie error = ENOSYS; 159353913Sarchie break; 159453913Sarchie } 159553913Sarchie } 159653913Sarchie 159753913Sarchie /* Convert command name to binary */ 159853913Sarchie binary->header.cmd = c->cmd; 159953913Sarchie binary->header.typecookie = c->cookie; 160053913Sarchie 160153913Sarchie /* Convert command arguments to binary */ 160253913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 160353913Sarchie c->respType : c->mesgType; 160453913Sarchie if (argstype == NULL) 160553913Sarchie bufSize = 0; 160653913Sarchie else { 160753913Sarchie if ((error = ng_parse(argstype, ascii->data, 160853913Sarchie &off, (u_char *)binary->data, &bufSize)) != 0) { 160953913Sarchie FREE(rp, M_NETGRAPH); 161053913Sarchie break; 161153913Sarchie } 161253913Sarchie } 161353913Sarchie 161453913Sarchie /* Return the result */ 161553913Sarchie binary->header.arglen = bufSize; 161653913Sarchie rp->header.arglen = sizeof(*binary) + bufSize; 161753913Sarchie *resp = rp; 161853913Sarchie break; 161953913Sarchie } 162053913Sarchie 162162471Sphk case NGM_TEXT_CONFIG: 162252419Sjulian case NGM_TEXT_STATUS: 162352419Sjulian /* 162452419Sjulian * This one is tricky as it passes the command down to the 162552419Sjulian * actual node, even though it is a generic type command. 162652419Sjulian * This means we must assume that the msg is already freed 162752419Sjulian * when control passes back to us. 162852419Sjulian */ 162952419Sjulian if (resp == NULL) { 163052419Sjulian error = EINVAL; 163152419Sjulian break; 163252419Sjulian } 163352419Sjulian if (here->type->rcvmsg != NULL) 163459728Sjulian return((*here->type->rcvmsg)(here, msg, retaddr, 163559728Sjulian resp, lasthook)); 163652419Sjulian /* Fall through if rcvmsg not supported */ 163752419Sjulian default: 163852419Sjulian TRAP_ERROR; 163952419Sjulian error = EINVAL; 164052419Sjulian } 164152419Sjulian FREE(msg, M_NETGRAPH); 164252419Sjulian return (error); 164352419Sjulian} 164452419Sjulian 164552419Sjulian/* 164652419Sjulian * Send a data packet to a node. If the recipient has no 164752419Sjulian * 'receive data' method, then silently discard the packet. 164869922Sjulian * The receiving node may elect to put the data onto the netgraph 164969922Sjulian * NETISR queue for later delivery. It may do this because it knows there 165069922Sjulian * is some recursion and wishes to unwind the stack, or because it has 165169922Sjulian * some suspicion that it is being called at (say) splimp instead of 165269922Sjulian * splnet. 165352419Sjulian */ 165452419Sjulianint 165559728Sjulianng_send_data(hook_p hook, struct mbuf *m, meta_p meta, 165669922Sjulian struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) 165752419Sjulian{ 165859728Sjulian ng_rcvdata_t *rcvdata; 165952419Sjulian 166053403Sarchie CHECK_DATA_MBUF(m); 166169922Sjulian if ((hook == NULL) 166269922Sjulian || ((hook->flags & HK_INVALID) != 0) 166369922Sjulian || ((rcvdata = hook->peer->node->type->rcvdata) == NULL)) { 166452419Sjulian TRAP_ERROR; 166552419Sjulian NG_FREE_DATA(m, meta); 166669922Sjulian return (ENOTCONN); 166752419Sjulian } 166869922Sjulian if (hook->peer->flags & HK_QUEUE) { 166969922Sjulian return (ng_queue_data(hook, m, meta)); 167069922Sjulian } 167169922Sjulian return ( (*rcvdata)(hook->peer, m, meta, ret_m, ret_meta, resp)); 167252419Sjulian} 167352419Sjulian 167452419Sjulian/* 167569922Sjulian * Send a queued data packet to a node. 167669922Sjulian * 167769922Sjulian * This is meant for data that is being dequeued and should therefore NOT 167869922Sjulian * be queued again. It ignores the queue flag and should NOT be called 167969922Sjulian * outside of this file. (thus it is static) 168052419Sjulian */ 168169922Sjulianstatic int 168269922Sjulianng_send_data_dont_queue(hook_p hook, struct mbuf *m, meta_p meta, 168369922Sjulian struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) 168452419Sjulian{ 168559728Sjulian ng_rcvdata_t *rcvdata; 168652419Sjulian 168753403Sarchie CHECK_DATA_MBUF(m); 168869922Sjulian if ((hook == NULL) 168969922Sjulian || ((hook->flags & HK_INVALID) != 0) 169069922Sjulian || ((rcvdata = hook->peer->node->type->rcvdata) == NULL)) { 169152419Sjulian TRAP_ERROR; 169252419Sjulian NG_FREE_DATA(m, meta); 169369922Sjulian return (ENOTCONN); 169469922Sjulian } 169569922Sjulian return ((*rcvdata)(hook->peer, m, meta, ret_m, ret_meta, resp)); 169652419Sjulian} 169752419Sjulian 169859879Sarchie/* 169959879Sarchie * Copy a 'meta'. 170059879Sarchie * 170159879Sarchie * Returns new meta, or NULL if original meta is NULL or ENOMEM. 170259879Sarchie */ 170359879Sarchiemeta_p 170459879Sarchieng_copy_meta(meta_p meta) 170559879Sarchie{ 170659879Sarchie meta_p meta2; 170759879Sarchie 170859879Sarchie if (meta == NULL) 170959879Sarchie return (NULL); 171059879Sarchie MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH, M_NOWAIT); 171159879Sarchie if (meta2 == NULL) 171259879Sarchie return (NULL); 171359879Sarchie meta2->allocated_len = meta->used_len; 171459879Sarchie bcopy(meta, meta2, meta->used_len); 171559879Sarchie return (meta2); 171659879Sarchie} 171759879Sarchie 171852419Sjulian/************************************************************************ 171952419Sjulian Module routines 172052419Sjulian************************************************************************/ 172152419Sjulian 172252419Sjulian/* 172352419Sjulian * Handle the loading/unloading of a netgraph node type module 172452419Sjulian */ 172552419Sjulianint 172652419Sjulianng_mod_event(module_t mod, int event, void *data) 172752419Sjulian{ 172852419Sjulian struct ng_type *const type = data; 172952419Sjulian int s, error = 0; 173052419Sjulian 173152419Sjulian switch (event) { 173252419Sjulian case MOD_LOAD: 173352419Sjulian 173452419Sjulian /* Register new netgraph node type */ 173552419Sjulian s = splnet(); 173652419Sjulian if ((error = ng_newtype(type)) != 0) { 173752419Sjulian splx(s); 173852419Sjulian break; 173952419Sjulian } 174052419Sjulian 174152419Sjulian /* Call type specific code */ 174252419Sjulian if (type->mod_event != NULL) 174352419Sjulian if ((error = (*type->mod_event)(mod, event, data)) != 0) 174452419Sjulian LIST_REMOVE(type, types); 174552419Sjulian splx(s); 174652419Sjulian break; 174752419Sjulian 174852419Sjulian case MOD_UNLOAD: 174952419Sjulian s = splnet(); 175052419Sjulian if (type->refs != 0) /* make sure no nodes exist! */ 175152419Sjulian error = EBUSY; 175252419Sjulian else { 175352419Sjulian if (type->mod_event != NULL) { /* check with type */ 175452419Sjulian error = (*type->mod_event)(mod, event, data); 175552419Sjulian if (error != 0) { /* type refuses.. */ 175652419Sjulian splx(s); 175752419Sjulian break; 175852419Sjulian } 175952419Sjulian } 176052419Sjulian LIST_REMOVE(type, types); 176152419Sjulian } 176252419Sjulian splx(s); 176352419Sjulian break; 176452419Sjulian 176552419Sjulian default: 176652419Sjulian if (type->mod_event != NULL) 176752419Sjulian error = (*type->mod_event)(mod, event, data); 176852419Sjulian else 176952419Sjulian error = 0; /* XXX ? */ 177052419Sjulian break; 177152419Sjulian } 177252419Sjulian return (error); 177352419Sjulian} 177452419Sjulian 177552419Sjulian/* 177652419Sjulian * Handle loading and unloading for this code. 177752419Sjulian * The only thing we need to link into is the NETISR strucure. 177852419Sjulian */ 177952419Sjulianstatic int 178052419Sjulianngb_mod_event(module_t mod, int event, void *data) 178152419Sjulian{ 178252419Sjulian int s, error = 0; 178352419Sjulian 178452419Sjulian switch (event) { 178552419Sjulian case MOD_LOAD: 178652419Sjulian /* Register line discipline */ 178752419Sjulian s = splimp(); 178852419Sjulian error = register_netisr(NETISR_NETGRAPH, ngintr); 178952419Sjulian splx(s); 179052419Sjulian break; 179152419Sjulian case MOD_UNLOAD: 179252419Sjulian /* You cant unload it because an interface may be using it. */ 179352419Sjulian error = EBUSY; 179452419Sjulian break; 179552419Sjulian default: 179652419Sjulian error = EOPNOTSUPP; 179752419Sjulian break; 179852419Sjulian } 179952419Sjulian return (error); 180052419Sjulian} 180152419Sjulian 180252419Sjulianstatic moduledata_t netgraph_mod = { 180352419Sjulian "netgraph", 180452419Sjulian ngb_mod_event, 180552419Sjulian (NULL) 180652419Sjulian}; 180752419SjulianDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 180852419Sjulian 180952419Sjulian/************************************************************************ 181052419Sjulian Queueing routines 181152419Sjulian************************************************************************/ 181252419Sjulian 181352419Sjulian/* The structure for queueing across ISR switches */ 181452419Sjulianstruct ng_queue_entry { 181552419Sjulian u_long flags; 181652419Sjulian struct ng_queue_entry *next; 181752419Sjulian union { 181852419Sjulian struct { 181952419Sjulian hook_p da_hook; /* target hook */ 182052419Sjulian struct mbuf *da_m; 182152419Sjulian meta_p da_meta; 182252419Sjulian } data; 182352419Sjulian struct { 182452419Sjulian struct ng_mesg *msg_msg; 182552419Sjulian node_p msg_node; 182659728Sjulian hook_p msg_lasthook; 182769922Sjulian char *msg_retaddr; 182852419Sjulian } msg; 182952419Sjulian } body; 183052419Sjulian}; 183152419Sjulian#define NGQF_DATA 0x01 /* the queue element is data */ 183252419Sjulian#define NGQF_MESG 0x02 /* the queue element is a message */ 183352419Sjulian 183452419Sjulianstatic struct ng_queue_entry *ngqbase; /* items to be unqueued */ 183552419Sjulianstatic struct ng_queue_entry *ngqlast; /* last item queued */ 183652419Sjulianstatic const int ngqroom = 64; /* max items to queue */ 183752419Sjulianstatic int ngqsize; /* number of items in queue */ 183852419Sjulian 183952419Sjulianstatic struct ng_queue_entry *ngqfree; /* free ones */ 184052419Sjulianstatic const int ngqfreemax = 16;/* cache at most this many */ 184152419Sjulianstatic int ngqfreesize; /* number of cached entries */ 184252419Sjulian 184352419Sjulian/* 184452419Sjulian * Get a queue entry 184552419Sjulian */ 184652419Sjulianstatic struct ng_queue_entry * 184752419Sjulianng_getqblk(void) 184852419Sjulian{ 184952419Sjulian register struct ng_queue_entry *q; 185052419Sjulian int s; 185152419Sjulian 185252419Sjulian /* Could be guarding against tty ints or whatever */ 185352419Sjulian s = splhigh(); 185452419Sjulian 185552419Sjulian /* Try get a cached queue block, or else allocate a new one */ 185652419Sjulian if ((q = ngqfree) == NULL) { 185752419Sjulian splx(s); 185852419Sjulian if (ngqsize < ngqroom) { /* don't worry about races */ 185952419Sjulian MALLOC(q, struct ng_queue_entry *, 186052419Sjulian sizeof(*q), M_NETGRAPH, M_NOWAIT); 186152419Sjulian } 186252419Sjulian } else { 186352419Sjulian ngqfree = q->next; 186452419Sjulian ngqfreesize--; 186552419Sjulian splx(s); 186652419Sjulian } 186752419Sjulian return (q); 186852419Sjulian} 186952419Sjulian 187052419Sjulian/* 187152419Sjulian * Release a queue entry 187252419Sjulian */ 187352419Sjulian#define RETURN_QBLK(q) \ 187452419Sjuliando { \ 187552419Sjulian int s; \ 187652419Sjulian if (ngqfreesize < ngqfreemax) { /* don't worry about races */ \ 187752419Sjulian s = splhigh(); \ 187852419Sjulian (q)->next = ngqfree; \ 187952419Sjulian ngqfree = (q); \ 188052419Sjulian ngqfreesize++; \ 188152419Sjulian splx(s); \ 188252419Sjulian } else { \ 188352419Sjulian FREE((q), M_NETGRAPH); \ 188452419Sjulian } \ 188552419Sjulian} while (0) 188652419Sjulian 188752419Sjulian/* 188852419Sjulian * Running at a raised (but we don't know which) processor priority level, 188952419Sjulian * put the data onto a queue to be picked up by another PPL (probably splnet) 189052419Sjulian */ 189152419Sjulianint 189252419Sjulianng_queue_data(hook_p hook, struct mbuf *m, meta_p meta) 189352419Sjulian{ 189452419Sjulian struct ng_queue_entry *q; 189552419Sjulian int s; 189652419Sjulian 189752419Sjulian if (hook == NULL) { 189852419Sjulian NG_FREE_DATA(m, meta); 189952419Sjulian return (0); 190052419Sjulian } 190152419Sjulian if ((q = ng_getqblk()) == NULL) { 190252419Sjulian NG_FREE_DATA(m, meta); 190352419Sjulian return (ENOBUFS); 190452419Sjulian } 190552419Sjulian 190652419Sjulian /* Fill out the contents */ 190752419Sjulian q->flags = NGQF_DATA; 190852419Sjulian q->next = NULL; 190952419Sjulian q->body.data.da_hook = hook; 191052419Sjulian q->body.data.da_m = m; 191152419Sjulian q->body.data.da_meta = meta; 191269519Sjulian s = splhigh(); /* protect refs and queue */ 191352419Sjulian hook->refs++; /* don't let it go away while on the queue */ 191452419Sjulian 191552419Sjulian /* Put it on the queue */ 191652419Sjulian if (ngqbase) { 191752419Sjulian ngqlast->next = q; 191852419Sjulian } else { 191952419Sjulian ngqbase = q; 192052419Sjulian } 192152419Sjulian ngqlast = q; 192252419Sjulian ngqsize++; 192352419Sjulian splx(s); 192452419Sjulian 192552419Sjulian /* Schedule software interrupt to handle it later */ 192652419Sjulian schednetisr(NETISR_NETGRAPH); 192752419Sjulian return (0); 192852419Sjulian} 192952419Sjulian 193052419Sjulian/* 193152419Sjulian * Running at a raised (but we don't know which) processor priority level, 193252419Sjulian * put the msg onto a queue to be picked up by another PPL (probably splnet) 193369922Sjulian * Either specify an address, or a hook to traverse. 193469922Sjulian * The return address can be specified, or it will be pointed at this node. 193552419Sjulian */ 193652419Sjulianint 193769922Sjulianng_queue_msg(node_p here, struct ng_mesg *msg, const char *address, hook_p hook,char *retaddr) 193852419Sjulian{ 193952419Sjulian register struct ng_queue_entry *q; 194052419Sjulian int s; 194152419Sjulian node_p dest = NULL; 194252419Sjulian int error; 194359728Sjulian hook_p lasthook = NULL; 194452419Sjulian 194569922Sjulian /* 194669922Sjulian * Find the target node. 194769922Sjulian * If there is a HOOK argument, then use that in preference 194869922Sjulian * to the address. 194969922Sjulian */ 195069922Sjulian if (hook) { 195169922Sjulian lasthook = hook->peer; 195269922Sjulian dest = lasthook->node; 195369922Sjulian } else { 195469922Sjulian error = ng_path2node(here, address, &dest, &lasthook); 195569922Sjulian if (error) { 195669922Sjulian FREE(msg, M_NETGRAPH); 195769922Sjulian return (error); 195869922Sjulian } 195952419Sjulian } 196069922Sjulian 196169922Sjulian if (retaddr == NULL) { 196269922Sjulian /* 196369922Sjulian * Now fill out the return address, 196469922Sjulian * i.e. the name/ID of the sender. (If we didn't get one) 196569922Sjulian */ 196669922Sjulian MALLOC(retaddr, char *, NG_NODELEN + 2, M_NETGRAPH, M_NOWAIT); 196769922Sjulian if (retaddr == NULL) { 196869922Sjulian TRAP_ERROR; 196969922Sjulian return (ENOMEM); 197069922Sjulian } 197169922Sjulian if (here->name != NULL) 197269922Sjulian sprintf(retaddr, "%s:", here->name); 197369922Sjulian else 197469922Sjulian sprintf(retaddr, "[%x]:", ng_node2ID(here)); 197569922Sjulian } 197669922Sjulian 197752419Sjulian if ((q = ng_getqblk()) == NULL) { 197852419Sjulian FREE(msg, M_NETGRAPH); 197952419Sjulian if (retaddr) 198052419Sjulian FREE(retaddr, M_NETGRAPH); 198152419Sjulian return (ENOBUFS); 198252419Sjulian } 198352419Sjulian 198452419Sjulian /* Fill out the contents */ 198552419Sjulian q->flags = NGQF_MESG; 198652419Sjulian q->next = NULL; 198752419Sjulian q->body.msg.msg_node = dest; 198852419Sjulian q->body.msg.msg_msg = msg; 198969922Sjulian q->body.msg.msg_retaddr = retaddr; /* XXX malloc'd, give it away */ 199069922Sjulian q->body.msg.msg_lasthook = lasthook; /* XXX needs reference */ 199169519Sjulian s = splhigh(); /* protect refs and queue */ 199252419Sjulian dest->refs++; /* don't let it go away while on the queue */ 199359728Sjulian if (lasthook) 199459728Sjulian lasthook->refs++; /* same for the hook */ 199552419Sjulian 199652419Sjulian /* Put it on the queue */ 199752419Sjulian if (ngqbase) { 199852419Sjulian ngqlast->next = q; 199952419Sjulian } else { 200052419Sjulian ngqbase = q; 200152419Sjulian } 200252419Sjulian ngqlast = q; 200352419Sjulian ngqsize++; 200452419Sjulian splx(s); 200552419Sjulian 200652419Sjulian /* Schedule software interrupt to handle it later */ 200752419Sjulian schednetisr(NETISR_NETGRAPH); 200852419Sjulian return (0); 200952419Sjulian} 201052419Sjulian 201152419Sjulian/* 201252419Sjulian * Pick an item off the queue, process it, and dispose of the queue entry. 201352419Sjulian * Should be running at splnet. 201452419Sjulian */ 201552419Sjulianstatic void 201652419Sjulianngintr(void) 201752419Sjulian{ 201852419Sjulian hook_p hook; 201952419Sjulian struct ng_queue_entry *ngq; 202052419Sjulian struct mbuf *m; 202152419Sjulian meta_p meta; 202252419Sjulian void *retaddr; 202352419Sjulian struct ng_mesg *msg; 202452419Sjulian node_p node; 202552419Sjulian int error = 0; 202652419Sjulian int s; 202752419Sjulian 202852419Sjulian while (1) { 202952419Sjulian s = splhigh(); 203052419Sjulian if ((ngq = ngqbase)) { 203152419Sjulian ngqbase = ngq->next; 203252419Sjulian ngqsize--; 203352419Sjulian } 203452419Sjulian splx(s); 203552419Sjulian if (ngq == NULL) 203652419Sjulian return; 203752419Sjulian switch (ngq->flags) { 203852419Sjulian case NGQF_DATA: 203952419Sjulian hook = ngq->body.data.da_hook; 204052419Sjulian m = ngq->body.data.da_m; 204152419Sjulian meta = ngq->body.data.da_meta; 204252419Sjulian RETURN_QBLK(ngq); 204369922Sjulian ng_send_data_dont_queue(hook, m, meta, 204469922Sjulian NULL, NULL, NULL); 204569922Sjulian m = NULL; 204669922Sjulian meta = NULL; 204752419Sjulian ng_unref_hook(hook); 204852419Sjulian break; 204952419Sjulian case NGQF_MESG: 205052419Sjulian node = ngq->body.msg.msg_node; 205152419Sjulian msg = ngq->body.msg.msg_msg; 205252419Sjulian retaddr = ngq->body.msg.msg_retaddr; 205359728Sjulian hook = ngq->body.msg.msg_lasthook; 205452419Sjulian RETURN_QBLK(ngq); 205559728Sjulian if (hook) { 205669519Sjulian if ((hook->flags & HK_INVALID) != 0) { 205769519Sjulian /* If the hook has been zapped 205859728Sjulian then we can't use it */ 205959728Sjulian ng_unref_hook(hook); 206059728Sjulian hook = NULL; 206159728Sjulian } 206259728Sjulian } 206359728Sjulian /* similarly, if the node is a zombie.. */ 206452419Sjulian if (node->flags & NG_INVALID) { 206552419Sjulian FREE(msg, M_NETGRAPH); 206652419Sjulian } else { 206752419Sjulian CALL_MSG_HANDLER(error, node, msg, 206859728Sjulian retaddr, NULL, hook); 206952419Sjulian } 207069519Sjulian if (hook) 207169519Sjulian ng_unref_hook(hook); 207252419Sjulian ng_unref(node); 207352419Sjulian if (retaddr) 207452419Sjulian FREE(retaddr, M_NETGRAPH); 207552419Sjulian break; 207652419Sjulian default: 207752419Sjulian RETURN_QBLK(ngq); 207852419Sjulian } 207952419Sjulian } 208052419Sjulian} 208152419Sjulian 208252419Sjulian 2083