ng_base.c revision 53913
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 53913 1999-11-30 02:45:32Z 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> 5752419Sjulian#include <sys/socketvar.h> 5852843Sphk#include <sys/ctype.h> 5952816Sarchie#include <machine/limits.h> 6052419Sjulian 6152419Sjulian#include <net/netisr.h> 6252419Sjulian 6352419Sjulian#include <netgraph/ng_message.h> 6452419Sjulian#include <netgraph/netgraph.h> 6553913Sarchie#include <netgraph/ng_parse.h> 6652419Sjulian 6752419Sjulian/* List of all nodes */ 6852419Sjulianstatic LIST_HEAD(, ng_node) nodelist; 6952419Sjulian 7052419Sjulian/* List of installed types */ 7152419Sjulianstatic LIST_HEAD(, ng_type) typelist; 7252419Sjulian 7352722Sjulian/* Hash releted definitions */ 7452722Sjulian#define ID_HASH_SIZE 32 /* most systems wont need even this many */ 7552722Sjulianstatic LIST_HEAD(, ng_node) ID_hash[ID_HASH_SIZE]; 7652722Sjulian/* Don't nead to initialise them because it's a LIST */ 7752722Sjulian 7852419Sjulian/* Internal functions */ 7952419Sjulianstatic int ng_add_hook(node_p node, const char *name, hook_p * hookp); 8052419Sjulianstatic int ng_connect(hook_p hook1, hook_p hook2); 8152419Sjulianstatic void ng_disconnect_hook(hook_p hook); 8252419Sjulianstatic int ng_generic_msg(node_p here, struct ng_mesg *msg, 8352419Sjulian const char *retaddr, struct ng_mesg ** resp); 8452722Sjulianstatic ng_ID_t ng_decodeidname(const char *name); 8552419Sjulianstatic int ngb_mod_event(module_t mod, int event, void *data); 8652419Sjulianstatic void ngintr(void); 8752419Sjulian 8852419Sjulian/* Our own netgraph malloc type */ 8952419SjulianMALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); 9052419Sjulian 9152419Sjulian/* Set this to Debugger("X") to catch all errors as they occur */ 9252419Sjulian#ifndef TRAP_ERROR 9352419Sjulian#define TRAP_ERROR 9452419Sjulian#endif 9552419Sjulian 9652722Sjulianstatic ng_ID_t nextID = 1; 9752722Sjulian 9853403Sarchie#ifdef INVARIANTS 9953403Sarchie#define CHECK_DATA_MBUF(m) do { \ 10053403Sarchie struct mbuf *n; \ 10153403Sarchie int total; \ 10253403Sarchie \ 10353403Sarchie if (((m)->m_flags & M_PKTHDR) == 0) \ 10453403Sarchie panic("%s: !PKTHDR", __FUNCTION__); \ 10553403Sarchie for (total = 0, n = (m); n != NULL; n = n->m_next) \ 10653403Sarchie total += n->m_len; \ 10753403Sarchie if ((m)->m_pkthdr.len != total) { \ 10853403Sarchie panic("%s: %d != %d", \ 10953403Sarchie __FUNCTION__, (m)->m_pkthdr.len, total); \ 11053403Sarchie } \ 11153403Sarchie } while (0) 11253403Sarchie#else 11353403Sarchie#define CHECK_DATA_MBUF(m) 11453403Sarchie#endif 11552722Sjulian 11653403Sarchie 11752419Sjulian/************************************************************************ 11853913Sarchie Parse type definitions for generic messages 11953913Sarchie************************************************************************/ 12053913Sarchie 12153913Sarchie/* Handy structure parse type defining macro */ 12253913Sarchie#define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ 12353913Sarchiestatic const struct ng_parse_struct_info \ 12453913Sarchie ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args; \ 12553913Sarchiestatic const struct ng_parse_type ng_generic_ ## lo ## _type = { \ 12653913Sarchie &ng_parse_struct_type, \ 12753913Sarchie &ng_ ## lo ## _type_info \ 12853913Sarchie} 12953913Sarchie 13053913SarchieDEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); 13153913SarchieDEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); 13253913SarchieDEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); 13353913SarchieDEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); 13453913SarchieDEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); 13553913SarchieDEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); 13653913SarchieDEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); 13753913Sarchie 13853913Sarchie/* Get length of an array when the length is stored as a 32 bit 13953913Sarchie value immediately preceeding the array -- as with struct namelist 14053913Sarchie and struct typelist. */ 14153913Sarchiestatic int 14253913Sarchieng_generic_list_getLength(const struct ng_parse_type *type, 14353913Sarchie const u_char *start, const u_char *buf) 14453913Sarchie{ 14553913Sarchie return *((const u_int32_t *)(buf - 4)); 14653913Sarchie} 14753913Sarchie 14853913Sarchie/* Get length of the array of struct linkinfo inside a struct hooklist */ 14953913Sarchiestatic int 15053913Sarchieng_generic_linkinfo_getLength(const struct ng_parse_type *type, 15153913Sarchie const u_char *start, const u_char *buf) 15253913Sarchie{ 15353913Sarchie const struct hooklist *hl = (const struct hooklist *)start; 15453913Sarchie 15553913Sarchie return hl->nodeinfo.hooks; 15653913Sarchie} 15753913Sarchie 15853913Sarchie/* Array type for a variable length array of struct namelist */ 15953913Sarchiestatic const struct ng_parse_array_info ng_nodeinfoarray_type_info = { 16053913Sarchie &ng_generic_nodeinfo_type, 16153913Sarchie &ng_generic_list_getLength 16253913Sarchie}; 16353913Sarchiestatic const struct ng_parse_type ng_generic_nodeinfoarray_type = { 16453913Sarchie &ng_parse_array_type, 16553913Sarchie &ng_nodeinfoarray_type_info 16653913Sarchie}; 16753913Sarchie 16853913Sarchie/* Array type for a variable length array of struct typelist */ 16953913Sarchiestatic const struct ng_parse_array_info ng_typeinfoarray_type_info = { 17053913Sarchie &ng_generic_typeinfo_type, 17153913Sarchie &ng_generic_list_getLength 17253913Sarchie}; 17353913Sarchiestatic const struct ng_parse_type ng_generic_typeinfoarray_type = { 17453913Sarchie &ng_parse_array_type, 17553913Sarchie &ng_typeinfoarray_type_info 17653913Sarchie}; 17753913Sarchie 17853913Sarchie/* Array type for array of struct linkinfo in struct hooklist */ 17953913Sarchiestatic const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { 18053913Sarchie &ng_generic_linkinfo_type, 18153913Sarchie &ng_generic_linkinfo_getLength 18253913Sarchie}; 18353913Sarchiestatic const struct ng_parse_type ng_generic_linkinfo_array_type = { 18453913Sarchie &ng_parse_array_type, 18553913Sarchie &ng_generic_linkinfo_array_type_info 18653913Sarchie}; 18753913Sarchie 18853913SarchieDEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); 18953913SarchieDEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, 19053913Sarchie (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); 19153913SarchieDEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, 19253913Sarchie (&ng_generic_nodeinfoarray_type)); 19353913Sarchie 19453913Sarchie/* List of commands and how to convert arguments to/from ASCII */ 19553913Sarchiestatic const struct ng_cmdlist ng_generic_cmds[] = { 19653913Sarchie { 19753913Sarchie NGM_GENERIC_COOKIE, 19853913Sarchie NGM_SHUTDOWN, 19953913Sarchie "shutdown", 20053913Sarchie NULL, 20153913Sarchie NULL 20253913Sarchie }, 20353913Sarchie { 20453913Sarchie NGM_GENERIC_COOKIE, 20553913Sarchie NGM_MKPEER, 20653913Sarchie "mkpeer", 20753913Sarchie &ng_generic_mkpeer_type, 20853913Sarchie NULL 20953913Sarchie }, 21053913Sarchie { 21153913Sarchie NGM_GENERIC_COOKIE, 21253913Sarchie NGM_CONNECT, 21353913Sarchie "connect", 21453913Sarchie &ng_generic_connect_type, 21553913Sarchie NULL 21653913Sarchie }, 21753913Sarchie { 21853913Sarchie NGM_GENERIC_COOKIE, 21953913Sarchie NGM_NAME, 22053913Sarchie "name", 22153913Sarchie &ng_generic_name_type, 22253913Sarchie NULL 22353913Sarchie }, 22453913Sarchie { 22553913Sarchie NGM_GENERIC_COOKIE, 22653913Sarchie NGM_RMHOOK, 22753913Sarchie "rmhook", 22853913Sarchie &ng_generic_rmhook_type, 22953913Sarchie NULL 23053913Sarchie }, 23153913Sarchie { 23253913Sarchie NGM_GENERIC_COOKIE, 23353913Sarchie NGM_NODEINFO, 23453913Sarchie "nodeinfo", 23553913Sarchie NULL, 23653913Sarchie &ng_generic_nodeinfo_type 23753913Sarchie }, 23853913Sarchie { 23953913Sarchie NGM_GENERIC_COOKIE, 24053913Sarchie NGM_LISTHOOKS, 24153913Sarchie "listhooks", 24253913Sarchie NULL, 24353913Sarchie &ng_generic_hooklist_type 24453913Sarchie }, 24553913Sarchie { 24653913Sarchie NGM_GENERIC_COOKIE, 24753913Sarchie NGM_LISTNAMES, 24853913Sarchie "listnames", 24953913Sarchie NULL, 25053913Sarchie &ng_generic_listnodes_type /* same as NGM_LISTNODES */ 25153913Sarchie }, 25253913Sarchie { 25353913Sarchie NGM_GENERIC_COOKIE, 25453913Sarchie NGM_LISTNODES, 25553913Sarchie "listnodes", 25653913Sarchie NULL, 25753913Sarchie &ng_generic_listnodes_type 25853913Sarchie }, 25953913Sarchie { 26053913Sarchie NGM_GENERIC_COOKIE, 26153913Sarchie NGM_LISTTYPES, 26253913Sarchie "listtypes", 26353913Sarchie NULL, 26453913Sarchie &ng_generic_typeinfo_type 26553913Sarchie }, 26653913Sarchie { 26753913Sarchie NGM_GENERIC_COOKIE, 26853913Sarchie NGM_TEXT_STATUS, 26953913Sarchie "textstatus", 27053913Sarchie NULL, 27153913Sarchie &ng_parse_string_type 27253913Sarchie }, 27353913Sarchie { 27453913Sarchie NGM_GENERIC_COOKIE, 27553913Sarchie NGM_ASCII2BINARY, 27653913Sarchie "ascii2binary", 27753913Sarchie &ng_parse_ng_mesg_type, 27853913Sarchie &ng_parse_ng_mesg_type 27953913Sarchie }, 28053913Sarchie { 28153913Sarchie NGM_GENERIC_COOKIE, 28253913Sarchie NGM_BINARY2ASCII, 28353913Sarchie "binary2ascii", 28453913Sarchie &ng_parse_ng_mesg_type, 28553913Sarchie &ng_parse_ng_mesg_type 28653913Sarchie }, 28753913Sarchie { 0 } 28853913Sarchie}; 28953913Sarchie 29053913Sarchie/************************************************************************ 29152419Sjulian Node routines 29252419Sjulian************************************************************************/ 29352419Sjulian 29452419Sjulian/* 29552419Sjulian * Instantiate a node of the requested type 29652419Sjulian */ 29752419Sjulianint 29852419Sjulianng_make_node(const char *typename, node_p *nodepp) 29952419Sjulian{ 30052419Sjulian struct ng_type *type; 30152419Sjulian 30252419Sjulian /* Check that the type makes sense */ 30352419Sjulian if (typename == NULL) { 30452419Sjulian TRAP_ERROR; 30552419Sjulian return (EINVAL); 30652419Sjulian } 30752419Sjulian 30852419Sjulian /* Locate the node type */ 30952419Sjulian if ((type = ng_findtype(typename)) == NULL) { 31052419Sjulian char *path, filename[NG_TYPELEN + 4]; 31152419Sjulian linker_file_t lf; 31252419Sjulian int error; 31352419Sjulian 31452419Sjulian /* Not found, try to load it as a loadable module */ 31552419Sjulian snprintf(filename, sizeof(filename), "ng_%s.ko", typename); 31652419Sjulian if ((path = linker_search_path(filename)) == NULL) 31752419Sjulian return (ENXIO); 31852419Sjulian error = linker_load_file(path, &lf); 31952419Sjulian FREE(path, M_LINKER); 32052419Sjulian if (error != 0) 32152419Sjulian return (error); 32252419Sjulian lf->userrefs++; /* pretend loaded by the syscall */ 32352419Sjulian 32452419Sjulian /* Try again, as now the type should have linked itself in */ 32552419Sjulian if ((type = ng_findtype(typename)) == NULL) 32652419Sjulian return (ENXIO); 32752419Sjulian } 32852419Sjulian 32952419Sjulian /* Call the constructor */ 33052419Sjulian if (type->constructor != NULL) 33152419Sjulian return ((*type->constructor)(nodepp)); 33252419Sjulian else 33352419Sjulian return (ng_make_node_common(type, nodepp)); 33452419Sjulian} 33552419Sjulian 33652419Sjulian/* 33752419Sjulian * Generic node creation. Called by node constructors. 33852419Sjulian * The returned node has a reference count of 1. 33952419Sjulian */ 34052419Sjulianint 34152419Sjulianng_make_node_common(struct ng_type *type, node_p *nodepp) 34252419Sjulian{ 34352419Sjulian node_p node; 34452419Sjulian 34552419Sjulian /* Require the node type to have been already installed */ 34652419Sjulian if (ng_findtype(type->name) == NULL) { 34752419Sjulian TRAP_ERROR; 34852419Sjulian return (EINVAL); 34952419Sjulian } 35052419Sjulian 35152419Sjulian /* Make a node and try attach it to the type */ 35252419Sjulian MALLOC(node, node_p, sizeof(*node), M_NETGRAPH, M_WAITOK); 35352419Sjulian if (node == NULL) { 35452419Sjulian TRAP_ERROR; 35552419Sjulian return (ENOMEM); 35652419Sjulian } 35752419Sjulian bzero(node, sizeof(*node)); 35852419Sjulian node->type = type; 35952419Sjulian node->refs++; /* note reference */ 36052419Sjulian type->refs++; 36152419Sjulian 36252419Sjulian /* Link us into the node linked list */ 36352419Sjulian LIST_INSERT_HEAD(&nodelist, node, nodes); 36452419Sjulian 36552419Sjulian /* Initialize hook list for new node */ 36652419Sjulian LIST_INIT(&node->hooks); 36752419Sjulian 36852722Sjulian /* get an ID and put us in the hash chain */ 36952722Sjulian node->ID = nextID++; /* 137 per second for 1 year before wrap */ 37052722Sjulian LIST_INSERT_HEAD(&ID_hash[node->ID % ID_HASH_SIZE], node, idnodes); 37152722Sjulian 37252419Sjulian /* Done */ 37352419Sjulian *nodepp = node; 37452419Sjulian return (0); 37552419Sjulian} 37652419Sjulian 37752419Sjulian/* 37852419Sjulian * Forceably start the shutdown process on a node. Either call 37952419Sjulian * it's shutdown method, or do the default shutdown if there is 38052419Sjulian * no type-specific method. 38152419Sjulian * 38252419Sjulian * Persistent nodes must have a type-specific method which 38352419Sjulian * resets the NG_INVALID flag. 38452419Sjulian */ 38552419Sjulianvoid 38652419Sjulianng_rmnode(node_p node) 38752419Sjulian{ 38852419Sjulian /* Check if it's already shutting down */ 38952419Sjulian if ((node->flags & NG_INVALID) != 0) 39052419Sjulian return; 39152419Sjulian 39252419Sjulian /* Add an extra reference so it doesn't go away during this */ 39352419Sjulian node->refs++; 39452419Sjulian 39552419Sjulian /* Mark it invalid so any newcomers know not to try use it */ 39652419Sjulian node->flags |= NG_INVALID; 39752419Sjulian 39852419Sjulian /* Ask the type if it has anything to do in this case */ 39952419Sjulian if (node->type && node->type->shutdown) 40052419Sjulian (*node->type->shutdown)(node); 40152419Sjulian else { /* do the default thing */ 40252419Sjulian ng_unname(node); 40352419Sjulian ng_cutlinks(node); 40452419Sjulian ng_unref(node); 40552419Sjulian } 40652419Sjulian 40752419Sjulian /* Remove extra reference, possibly the last */ 40852419Sjulian ng_unref(node); 40952419Sjulian} 41052419Sjulian 41152419Sjulian/* 41252419Sjulian * Called by the destructor to remove any STANDARD external references 41352419Sjulian */ 41452419Sjulianvoid 41552419Sjulianng_cutlinks(node_p node) 41652419Sjulian{ 41752419Sjulian hook_p hook; 41852419Sjulian 41952419Sjulian /* Make sure that this is set to stop infinite loops */ 42052419Sjulian node->flags |= NG_INVALID; 42152419Sjulian 42252419Sjulian /* If we have sleepers, wake them up; they'll see NG_INVALID */ 42352419Sjulian if (node->sleepers) 42452419Sjulian wakeup(node); 42552419Sjulian 42652419Sjulian /* Notify all remaining connected nodes to disconnect */ 42752419Sjulian while ((hook = LIST_FIRST(&node->hooks)) != NULL) 42852419Sjulian ng_destroy_hook(hook); 42952419Sjulian} 43052419Sjulian 43152419Sjulian/* 43252419Sjulian * Remove a reference to the node, possibly the last 43352419Sjulian */ 43452419Sjulianvoid 43552419Sjulianng_unref(node_p node) 43652419Sjulian{ 43752419Sjulian if (--node->refs <= 0) { 43852419Sjulian node->type->refs--; 43952419Sjulian LIST_REMOVE(node, nodes); 44052722Sjulian LIST_REMOVE(node, idnodes); 44152419Sjulian FREE(node, M_NETGRAPH); 44252419Sjulian } 44352419Sjulian} 44452419Sjulian 44552419Sjulian/* 44652419Sjulian * Wait for a node to come ready. Returns a node with a reference count; 44752419Sjulian * don't forget to drop it when we are done with it using ng_release_node(). 44852419Sjulian */ 44952419Sjulianint 45052419Sjulianng_wait_node(node_p node, char *msg) 45152419Sjulian{ 45252419Sjulian int s, error = 0; 45352419Sjulian 45452419Sjulian if (msg == NULL) 45552419Sjulian msg = "netgraph"; 45652419Sjulian s = splnet(); 45752419Sjulian node->sleepers++; 45852419Sjulian node->refs++; /* the sleeping process counts as a reference */ 45952419Sjulian while ((node->flags & (NG_BUSY | NG_INVALID)) == NG_BUSY) 46052419Sjulian error = tsleep(node, (PZERO + 1) | PCATCH, msg, 0); 46152419Sjulian node->sleepers--; 46252419Sjulian if (node->flags & NG_INVALID) { 46352419Sjulian TRAP_ERROR; 46452419Sjulian error = ENXIO; 46552419Sjulian } else { 46653403Sarchie KASSERT(node->refs > 1, 46753403Sarchie ("%s: refs=%d", __FUNCTION__, node->refs)); 46852419Sjulian node->flags |= NG_BUSY; 46952419Sjulian } 47052419Sjulian splx(s); 47152419Sjulian 47252419Sjulian /* Release the reference we had on it */ 47352419Sjulian if (error != 0) 47452419Sjulian ng_unref(node); 47552419Sjulian return error; 47652419Sjulian} 47752419Sjulian 47852419Sjulian/* 47952419Sjulian * Release a node acquired via ng_wait_node() 48052419Sjulian */ 48152419Sjulianvoid 48252419Sjulianng_release_node(node_p node) 48352419Sjulian{ 48452419Sjulian /* Declare that we don't want it */ 48552419Sjulian node->flags &= ~NG_BUSY; 48652419Sjulian 48752419Sjulian /* If we have sleepers, then wake them up */ 48852419Sjulian if (node->sleepers) 48952419Sjulian wakeup(node); 49052419Sjulian 49152419Sjulian /* We also have a reference.. drop it too */ 49252419Sjulian ng_unref(node); 49352419Sjulian} 49452419Sjulian 49552419Sjulian/************************************************************************ 49652722Sjulian Node ID handling 49752722Sjulian************************************************************************/ 49852722Sjulianstatic node_p 49952722Sjulianng_ID2node(ng_ID_t ID) 50052722Sjulian{ 50152722Sjulian node_p np; 50252722Sjulian LIST_FOREACH(np, &ID_hash[ID % ID_HASH_SIZE], idnodes) { 50352722Sjulian if (np->ID == ID) 50452722Sjulian break; 50552722Sjulian } 50652722Sjulian return(np); 50752722Sjulian} 50852722Sjulian 50952722Sjulianng_ID_t 51052722Sjulianng_node2ID(node_p node) 51152722Sjulian{ 51252722Sjulian return (node->ID); 51352722Sjulian} 51452722Sjulian 51552722Sjulian/************************************************************************ 51652419Sjulian Node name handling 51752419Sjulian************************************************************************/ 51852419Sjulian 51952419Sjulian/* 52052419Sjulian * Assign a node a name. Once assigned, the name cannot be changed. 52152419Sjulian */ 52252419Sjulianint 52352419Sjulianng_name_node(node_p node, const char *name) 52452419Sjulian{ 52552419Sjulian int i; 52652419Sjulian 52752419Sjulian /* Check the name is valid */ 52852419Sjulian for (i = 0; i < NG_NODELEN + 1; i++) { 52952419Sjulian if (name[i] == '\0' || name[i] == '.' || name[i] == ':') 53052419Sjulian break; 53152419Sjulian } 53252419Sjulian if (i == 0 || name[i] != '\0') { 53352419Sjulian TRAP_ERROR; 53452419Sjulian return (EINVAL); 53552419Sjulian } 53652722Sjulian if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ 53752419Sjulian TRAP_ERROR; 53852419Sjulian return (EINVAL); 53952419Sjulian } 54052419Sjulian 54152419Sjulian /* Check the node isn't already named */ 54252419Sjulian if (node->name != NULL) { 54352419Sjulian TRAP_ERROR; 54452419Sjulian return (EISCONN); 54552419Sjulian } 54652419Sjulian 54752419Sjulian /* Check the name isn't already being used */ 54852419Sjulian if (ng_findname(node, name) != NULL) { 54952419Sjulian TRAP_ERROR; 55052419Sjulian return (EADDRINUSE); 55152419Sjulian } 55252419Sjulian 55352419Sjulian /* Allocate space and copy it */ 55452419Sjulian MALLOC(node->name, char *, strlen(name) + 1, M_NETGRAPH, M_WAITOK); 55552419Sjulian if (node->name == NULL) { 55652419Sjulian TRAP_ERROR; 55752419Sjulian return (ENOMEM); 55852419Sjulian } 55952419Sjulian strcpy(node->name, name); 56052419Sjulian 56152419Sjulian /* The name counts as a reference */ 56252419Sjulian node->refs++; 56352419Sjulian return (0); 56452419Sjulian} 56552419Sjulian 56652419Sjulian/* 56752419Sjulian * Find a node by absolute name. The name should NOT end with ':' 56852419Sjulian * The name "." means "this node" and "[xxx]" means "the node 56952419Sjulian * with ID (ie, at address) xxx". 57052419Sjulian * 57152419Sjulian * Returns the node if found, else NULL. 57252419Sjulian */ 57352419Sjuliannode_p 57452419Sjulianng_findname(node_p this, const char *name) 57552419Sjulian{ 57652722Sjulian node_p node; 57752722Sjulian ng_ID_t temp; 57852419Sjulian 57952419Sjulian /* "." means "this node" */ 58052419Sjulian if (strcmp(name, ".") == 0) 58152419Sjulian return(this); 58252419Sjulian 58352419Sjulian /* Check for name-by-ID */ 58452722Sjulian if ((temp = ng_decodeidname(name)) != 0) { 58552722Sjulian return (ng_ID2node(temp)); 58652419Sjulian } 58752419Sjulian 58852419Sjulian /* Find node by name */ 58952419Sjulian LIST_FOREACH(node, &nodelist, nodes) { 59052419Sjulian if (node->name != NULL && strcmp(node->name, name) == 0) 59152419Sjulian break; 59252419Sjulian } 59352419Sjulian return (node); 59452419Sjulian} 59552419Sjulian 59652419Sjulian/* 59752722Sjulian * Decode a ID name, eg. "[f03034de]". Returns 0 if the 59852722Sjulian * string is not valid, otherwise returns the value. 59952419Sjulian */ 60052722Sjulianstatic ng_ID_t 60152419Sjulianng_decodeidname(const char *name) 60252419Sjulian{ 60352816Sarchie const int len = strlen(name); 60453648Sarchie char *eptr; 60552816Sarchie u_long val; 60652419Sjulian 60752816Sarchie /* Check for proper length, brackets, no leading junk */ 60852816Sarchie if (len < 3 || name[0] != '[' || name[len - 1] != ']' 60952951Sjulian || !isxdigit(name[1])) 61052816Sarchie return (0); 61152419Sjulian 61252816Sarchie /* Decode number */ 61352816Sarchie val = strtoul(name + 1, &eptr, 16); 61452951Sjulian if (eptr - name != len - 1 || val == ULONG_MAX || val == 0) 61553042Sjulian return ((ng_ID_t)0); 61652816Sarchie return (ng_ID_t)val; 61752419Sjulian} 61852419Sjulian 61952419Sjulian/* 62052419Sjulian * Remove a name from a node. This should only be called 62152419Sjulian * when shutting down and removing the node. 62252419Sjulian */ 62352419Sjulianvoid 62452419Sjulianng_unname(node_p node) 62552419Sjulian{ 62652419Sjulian if (node->name) { 62752419Sjulian FREE(node->name, M_NETGRAPH); 62852419Sjulian node->name = NULL; 62952419Sjulian ng_unref(node); 63052419Sjulian } 63152419Sjulian} 63252419Sjulian 63352419Sjulian/************************************************************************ 63452419Sjulian Hook routines 63552419Sjulian 63652419Sjulian Names are not optional. Hooks are always connected, except for a 63752419Sjulian brief moment within these routines. 63852419Sjulian 63952419Sjulian************************************************************************/ 64052419Sjulian 64152419Sjulian/* 64252419Sjulian * Remove a hook reference 64352419Sjulian */ 64452419Sjulianstatic void 64552419Sjulianng_unref_hook(hook_p hook) 64652419Sjulian{ 64752419Sjulian if (--hook->refs == 0) 64852419Sjulian FREE(hook, M_NETGRAPH); 64952419Sjulian} 65052419Sjulian 65152419Sjulian/* 65252419Sjulian * Add an unconnected hook to a node. Only used internally. 65352419Sjulian */ 65452419Sjulianstatic int 65552419Sjulianng_add_hook(node_p node, const char *name, hook_p *hookp) 65652419Sjulian{ 65752419Sjulian hook_p hook; 65852419Sjulian int error = 0; 65952419Sjulian 66052419Sjulian /* Check that the given name is good */ 66152419Sjulian if (name == NULL) { 66252419Sjulian TRAP_ERROR; 66352419Sjulian return (EINVAL); 66452419Sjulian } 66552419Sjulian LIST_FOREACH(hook, &node->hooks, hooks) { 66652419Sjulian if (strcmp(hook->name, name) == 0) { 66752419Sjulian TRAP_ERROR; 66852419Sjulian return (EEXIST); 66952419Sjulian } 67052419Sjulian } 67152419Sjulian 67252419Sjulian /* Allocate the hook and link it up */ 67352419Sjulian MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH, M_WAITOK); 67452419Sjulian if (hook == NULL) { 67552419Sjulian TRAP_ERROR; 67652419Sjulian return (ENOMEM); 67752419Sjulian } 67852419Sjulian bzero(hook, sizeof(*hook)); 67952419Sjulian hook->refs = 1; 68052419Sjulian hook->flags = HK_INVALID; 68152419Sjulian hook->node = node; 68252419Sjulian node->refs++; /* each hook counts as a reference */ 68352419Sjulian 68452419Sjulian /* Check if the node type code has something to say about it */ 68552419Sjulian if (node->type->newhook != NULL) 68652419Sjulian if ((error = (*node->type->newhook)(node, hook, name)) != 0) 68752419Sjulian goto fail; 68852419Sjulian 68952419Sjulian /* 69052419Sjulian * The 'type' agrees so far, so go ahead and link it in. 69152419Sjulian * We'll ask again later when we actually connect the hooks. 69252419Sjulian */ 69352419Sjulian LIST_INSERT_HEAD(&node->hooks, hook, hooks); 69452419Sjulian node->numhooks++; 69552419Sjulian 69652419Sjulian /* Set hook name */ 69752419Sjulian MALLOC(hook->name, char *, strlen(name) + 1, M_NETGRAPH, M_WAITOK); 69852419Sjulian if (hook->name == NULL) { 69952419Sjulian error = ENOMEM; 70052419Sjulian LIST_REMOVE(hook, hooks); 70152419Sjulian node->numhooks--; 70252419Sjulianfail: 70352419Sjulian hook->node = NULL; 70452419Sjulian ng_unref(node); 70552419Sjulian ng_unref_hook(hook); /* this frees the hook */ 70652419Sjulian return (error); 70752419Sjulian } 70852419Sjulian strcpy(hook->name, name); 70952419Sjulian if (hookp) 71052419Sjulian *hookp = hook; 71152419Sjulian return (error); 71252419Sjulian} 71352419Sjulian 71452419Sjulian/* 71552419Sjulian * Connect a pair of hooks. Only used internally. 71652419Sjulian */ 71752419Sjulianstatic int 71852419Sjulianng_connect(hook_p hook1, hook_p hook2) 71952419Sjulian{ 72052419Sjulian int error; 72152419Sjulian 72252419Sjulian hook1->peer = hook2; 72352419Sjulian hook2->peer = hook1; 72452419Sjulian 72552419Sjulian /* Give each node the opportunity to veto the impending connection */ 72652419Sjulian if (hook1->node->type->connect) { 72752419Sjulian if ((error = (*hook1->node->type->connect) (hook1))) { 72852419Sjulian ng_destroy_hook(hook1); /* also zaps hook2 */ 72952419Sjulian return (error); 73052419Sjulian } 73152419Sjulian } 73252419Sjulian if (hook2->node->type->connect) { 73352419Sjulian if ((error = (*hook2->node->type->connect) (hook2))) { 73452419Sjulian ng_destroy_hook(hook2); /* also zaps hook1 */ 73552419Sjulian return (error); 73652419Sjulian } 73752419Sjulian } 73852419Sjulian hook1->flags &= ~HK_INVALID; 73952419Sjulian hook2->flags &= ~HK_INVALID; 74052419Sjulian return (0); 74152419Sjulian} 74252419Sjulian 74352419Sjulian/* 74452419Sjulian * Destroy a hook 74552419Sjulian * 74652419Sjulian * As hooks are always attached, this really destroys two hooks. 74752419Sjulian * The one given, and the one attached to it. Disconnect the hooks 74852419Sjulian * from each other first. 74952419Sjulian */ 75052419Sjulianvoid 75152419Sjulianng_destroy_hook(hook_p hook) 75252419Sjulian{ 75352419Sjulian hook_p peer = hook->peer; 75452419Sjulian 75552419Sjulian hook->flags |= HK_INVALID; /* as soon as possible */ 75652419Sjulian if (peer) { 75752419Sjulian peer->flags |= HK_INVALID; /* as soon as possible */ 75852419Sjulian hook->peer = NULL; 75952419Sjulian peer->peer = NULL; 76052419Sjulian ng_disconnect_hook(peer); 76152419Sjulian } 76252419Sjulian ng_disconnect_hook(hook); 76352419Sjulian} 76452419Sjulian 76552419Sjulian/* 76652419Sjulian * Notify the node of the hook's demise. This may result in more actions 76752419Sjulian * (e.g. shutdown) but we don't do that ourselves and don't know what 76852419Sjulian * happens there. If there is no appropriate handler, then just remove it 76952419Sjulian * (and decrement the reference count of it's node which in turn might 77052419Sjulian * make something happen). 77152419Sjulian */ 77252419Sjulianstatic void 77352419Sjulianng_disconnect_hook(hook_p hook) 77452419Sjulian{ 77552419Sjulian node_p node = hook->node; 77652419Sjulian 77752419Sjulian /* 77852419Sjulian * Remove the hook from the node's list to avoid possible recursion 77952419Sjulian * in case the disconnection results in node shutdown. 78052419Sjulian */ 78152419Sjulian LIST_REMOVE(hook, hooks); 78252419Sjulian node->numhooks--; 78352419Sjulian if (node->type->disconnect) { 78452419Sjulian /* 78552419Sjulian * The type handler may elect to destroy the peer so don't 78652419Sjulian * trust its existance after this point. 78752419Sjulian */ 78852419Sjulian (*node->type->disconnect) (hook); 78952419Sjulian } 79052419Sjulian ng_unref(node); /* might be the last reference */ 79152419Sjulian if (hook->name) 79252419Sjulian FREE(hook->name, M_NETGRAPH); 79352419Sjulian hook->node = NULL; /* may still be referenced elsewhere */ 79452419Sjulian ng_unref_hook(hook); 79552419Sjulian} 79652419Sjulian 79752419Sjulian/* 79852419Sjulian * Take two hooks on a node and merge the connection so that the given node 79952419Sjulian * is effectively bypassed. 80052419Sjulian */ 80152419Sjulianint 80252419Sjulianng_bypass(hook_p hook1, hook_p hook2) 80352419Sjulian{ 80452419Sjulian if (hook1->node != hook2->node) 80552419Sjulian return (EINVAL); 80652419Sjulian hook1->peer->peer = hook2->peer; 80752419Sjulian hook2->peer->peer = hook1->peer; 80852419Sjulian 80952419Sjulian /* XXX If we ever cache methods on hooks update them as well */ 81052419Sjulian hook1->peer = NULL; 81152419Sjulian hook2->peer = NULL; 81252419Sjulian ng_destroy_hook(hook1); 81352419Sjulian ng_destroy_hook(hook2); 81452419Sjulian return (0); 81552419Sjulian} 81652419Sjulian 81752419Sjulian/* 81852419Sjulian * Install a new netgraph type 81952419Sjulian */ 82052419Sjulianint 82152419Sjulianng_newtype(struct ng_type *tp) 82252419Sjulian{ 82352419Sjulian const size_t namelen = strlen(tp->name); 82452419Sjulian 82552419Sjulian /* Check version and type name fields */ 82652419Sjulian if (tp->version != NG_VERSION || namelen == 0 || namelen > NG_TYPELEN) { 82752419Sjulian TRAP_ERROR; 82852419Sjulian return (EINVAL); 82952419Sjulian } 83052419Sjulian 83152419Sjulian /* Check for name collision */ 83252419Sjulian if (ng_findtype(tp->name) != NULL) { 83352419Sjulian TRAP_ERROR; 83452419Sjulian return (EEXIST); 83552419Sjulian } 83652419Sjulian 83752419Sjulian /* Link in new type */ 83852419Sjulian LIST_INSERT_HEAD(&typelist, tp, types); 83952419Sjulian tp->refs = 0; 84052419Sjulian return (0); 84152419Sjulian} 84252419Sjulian 84352419Sjulian/* 84452419Sjulian * Look for a type of the name given 84552419Sjulian */ 84652419Sjulianstruct ng_type * 84752419Sjulianng_findtype(const char *typename) 84852419Sjulian{ 84952419Sjulian struct ng_type *type; 85052419Sjulian 85152419Sjulian LIST_FOREACH(type, &typelist, types) { 85252419Sjulian if (strcmp(type->name, typename) == 0) 85352419Sjulian break; 85452419Sjulian } 85552419Sjulian return (type); 85652419Sjulian} 85752419Sjulian 85852419Sjulian 85952419Sjulian/************************************************************************ 86052419Sjulian Composite routines 86152419Sjulian************************************************************************/ 86252419Sjulian 86352419Sjulian/* 86452419Sjulian * Make a peer and connect. The order is arranged to minimise 86552419Sjulian * the work needed to back out in case of error. 86652419Sjulian */ 86752419Sjulianint 86852419Sjulianng_mkpeer(node_p node, const char *name, const char *name2, char *type) 86952419Sjulian{ 87052419Sjulian node_p node2; 87152419Sjulian hook_p hook; 87252419Sjulian hook_p hook2; 87352419Sjulian int error; 87452419Sjulian 87552419Sjulian if ((error = ng_add_hook(node, name, &hook))) 87652419Sjulian return (error); 87752419Sjulian if ((error = ng_make_node(type, &node2))) { 87852419Sjulian ng_destroy_hook(hook); 87952419Sjulian return (error); 88052419Sjulian } 88152419Sjulian if ((error = ng_add_hook(node2, name2, &hook2))) { 88252419Sjulian ng_rmnode(node2); 88352419Sjulian ng_destroy_hook(hook); 88452419Sjulian return (error); 88552419Sjulian } 88652419Sjulian 88752419Sjulian /* 88852419Sjulian * Actually link the two hooks together.. on failure they are 88952419Sjulian * destroyed so we don't have to do that here. 89052419Sjulian */ 89152419Sjulian if ((error = ng_connect(hook, hook2))) 89252419Sjulian ng_rmnode(node2); 89352419Sjulian return (error); 89452419Sjulian} 89552419Sjulian 89652419Sjulian/* 89752419Sjulian * Connect two nodes using the specified hooks 89852419Sjulian */ 89952419Sjulianint 90052419Sjulianng_con_nodes(node_p node, const char *name, node_p node2, const char *name2) 90152419Sjulian{ 90252419Sjulian int error; 90352419Sjulian hook_p hook; 90452419Sjulian hook_p hook2; 90552419Sjulian 90652419Sjulian if ((error = ng_add_hook(node, name, &hook))) 90752419Sjulian return (error); 90852419Sjulian if ((error = ng_add_hook(node2, name2, &hook2))) { 90952419Sjulian ng_destroy_hook(hook); 91052419Sjulian return (error); 91152419Sjulian } 91252419Sjulian return (ng_connect(hook, hook2)); 91352419Sjulian} 91452419Sjulian 91552419Sjulian/* 91652419Sjulian * Parse and verify a string of the form: <NODE:><PATH> 91752419Sjulian * 91852419Sjulian * Such a string can refer to a specific node or a specific hook 91952419Sjulian * on a specific node, depending on how you look at it. In the 92052419Sjulian * latter case, the PATH component must not end in a dot. 92152419Sjulian * 92252419Sjulian * Both <NODE:> and <PATH> are optional. The <PATH> is a string 92352419Sjulian * of hook names separated by dots. This breaks out the original 92452419Sjulian * string, setting *nodep to "NODE" (or NULL if none) and *pathp 92552419Sjulian * to "PATH" (or NULL if degenerate). Also, *hookp will point to 92652419Sjulian * the final hook component of <PATH>, if any, otherwise NULL. 92752419Sjulian * 92852419Sjulian * This returns -1 if the path is malformed. The char ** are optional. 92952419Sjulian */ 93052419Sjulian 93152419Sjulianint 93252419Sjulianng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 93352419Sjulian{ 93452419Sjulian char *node, *path, *hook; 93552419Sjulian int k; 93652419Sjulian 93752419Sjulian /* 93852419Sjulian * Extract absolute NODE, if any 93952419Sjulian */ 94052419Sjulian for (path = addr; *path && *path != ':'; path++); 94152419Sjulian if (*path) { 94252419Sjulian node = addr; /* Here's the NODE */ 94352419Sjulian *path++ = '\0'; /* Here's the PATH */ 94452419Sjulian 94552419Sjulian /* Node name must not be empty */ 94652419Sjulian if (!*node) 94752419Sjulian return -1; 94852419Sjulian 94952419Sjulian /* A name of "." is OK; otherwise '.' not allowed */ 95052419Sjulian if (strcmp(node, ".") != 0) { 95152419Sjulian for (k = 0; node[k]; k++) 95252419Sjulian if (node[k] == '.') 95352419Sjulian return -1; 95452419Sjulian } 95552419Sjulian } else { 95652419Sjulian node = NULL; /* No absolute NODE */ 95752419Sjulian path = addr; /* Here's the PATH */ 95852419Sjulian } 95952419Sjulian 96052419Sjulian /* Snoop for illegal characters in PATH */ 96152419Sjulian for (k = 0; path[k]; k++) 96252419Sjulian if (path[k] == ':') 96352419Sjulian return -1; 96452419Sjulian 96552419Sjulian /* Check for no repeated dots in PATH */ 96652419Sjulian for (k = 0; path[k]; k++) 96752419Sjulian if (path[k] == '.' && path[k + 1] == '.') 96852419Sjulian return -1; 96952419Sjulian 97052419Sjulian /* Remove extra (degenerate) dots from beginning or end of PATH */ 97152419Sjulian if (path[0] == '.') 97252419Sjulian path++; 97352419Sjulian if (*path && path[strlen(path) - 1] == '.') 97452419Sjulian path[strlen(path) - 1] = 0; 97552419Sjulian 97652419Sjulian /* If PATH has a dot, then we're not talking about a hook */ 97752419Sjulian if (*path) { 97852419Sjulian for (hook = path, k = 0; path[k]; k++) 97952419Sjulian if (path[k] == '.') { 98052419Sjulian hook = NULL; 98152419Sjulian break; 98252419Sjulian } 98352419Sjulian } else 98452419Sjulian path = hook = NULL; 98552419Sjulian 98652419Sjulian /* Done */ 98752419Sjulian if (nodep) 98852419Sjulian *nodep = node; 98952419Sjulian if (pathp) 99052419Sjulian *pathp = path; 99152419Sjulian if (hookp) 99252419Sjulian *hookp = hook; 99352419Sjulian return (0); 99452419Sjulian} 99552419Sjulian 99652419Sjulian/* 99752419Sjulian * Given a path, which may be absolute or relative, and a starting node, 99852419Sjulian * return the destination node. Compute the "return address" if desired. 99952419Sjulian */ 100052419Sjulianint 100152419Sjulianng_path2node(node_p here, const char *address, node_p *destp, char **rtnp) 100252419Sjulian{ 100352419Sjulian const node_p start = here; 100452419Sjulian char fullpath[NG_PATHLEN + 1]; 100552419Sjulian char *nodename, *path, pbuf[2]; 100652419Sjulian node_p node; 100752419Sjulian char *cp; 100852419Sjulian 100952419Sjulian /* Initialize */ 101052419Sjulian if (rtnp) 101152419Sjulian *rtnp = NULL; 101252419Sjulian if (destp == NULL) 101352419Sjulian return EINVAL; 101452419Sjulian *destp = NULL; 101552419Sjulian 101652419Sjulian /* Make a writable copy of address for ng_path_parse() */ 101752419Sjulian strncpy(fullpath, address, sizeof(fullpath) - 1); 101852419Sjulian fullpath[sizeof(fullpath) - 1] = '\0'; 101952419Sjulian 102052419Sjulian /* Parse out node and sequence of hooks */ 102152419Sjulian if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 102252419Sjulian TRAP_ERROR; 102352419Sjulian return EINVAL; 102452419Sjulian } 102552419Sjulian if (path == NULL) { 102652419Sjulian pbuf[0] = '.'; /* Needs to be writable */ 102752419Sjulian pbuf[1] = '\0'; 102852419Sjulian path = pbuf; 102952419Sjulian } 103052419Sjulian 103152419Sjulian /* For an absolute address, jump to the starting node */ 103252419Sjulian if (nodename) { 103352419Sjulian node = ng_findname(here, nodename); 103452419Sjulian if (node == NULL) { 103552419Sjulian TRAP_ERROR; 103652419Sjulian return (ENOENT); 103752419Sjulian } 103852419Sjulian } else 103952419Sjulian node = here; 104052419Sjulian 104152419Sjulian /* Now follow the sequence of hooks */ 104252419Sjulian for (cp = path; node != NULL && *cp != '\0'; ) { 104352419Sjulian hook_p hook; 104452419Sjulian char *segment; 104552419Sjulian 104652419Sjulian /* 104752419Sjulian * Break out the next path segment. Replace the dot we just 104852419Sjulian * found with a NUL; "cp" points to the next segment (or the 104952419Sjulian * NUL at the end). 105052419Sjulian */ 105152419Sjulian for (segment = cp; *cp != '\0'; cp++) { 105252419Sjulian if (*cp == '.') { 105352419Sjulian *cp++ = '\0'; 105452419Sjulian break; 105552419Sjulian } 105652419Sjulian } 105752419Sjulian 105852419Sjulian /* Empty segment */ 105952419Sjulian if (*segment == '\0') 106052419Sjulian continue; 106152419Sjulian 106252419Sjulian /* We have a segment, so look for a hook by that name */ 106352419Sjulian LIST_FOREACH(hook, &node->hooks, hooks) { 106452419Sjulian if (hook->name && strcmp(hook->name, segment) == 0) 106552419Sjulian break; 106652419Sjulian } 106752419Sjulian 106852419Sjulian /* Can't get there from here... */ 106952419Sjulian if (hook == NULL 107052419Sjulian || hook->peer == NULL 107152419Sjulian || (hook->flags & HK_INVALID) != 0) { 107252419Sjulian TRAP_ERROR; 107352419Sjulian return (ENOENT); 107452419Sjulian } 107552419Sjulian 107652419Sjulian /* Hop on over to the next node */ 107752419Sjulian node = hook->peer->node; 107852419Sjulian } 107952419Sjulian 108052419Sjulian /* If node somehow missing, fail here (probably this is not needed) */ 108152419Sjulian if (node == NULL) { 108252419Sjulian TRAP_ERROR; 108352419Sjulian return (ENXIO); 108452419Sjulian } 108552419Sjulian 108652419Sjulian /* Now compute return address, i.e., the path to the sender */ 108752419Sjulian if (rtnp != NULL) { 108852419Sjulian MALLOC(*rtnp, char *, NG_NODELEN + 2, M_NETGRAPH, M_WAITOK); 108952419Sjulian if (*rtnp == NULL) { 109052419Sjulian TRAP_ERROR; 109152419Sjulian return (ENOMEM); 109252419Sjulian } 109352419Sjulian if (start->name != NULL) 109452419Sjulian sprintf(*rtnp, "%s:", start->name); 109552419Sjulian else 109652722Sjulian sprintf(*rtnp, "[%x]:", ng_node2ID(start)); 109752419Sjulian } 109852419Sjulian 109952419Sjulian /* Done */ 110052419Sjulian *destp = node; 110152419Sjulian return (0); 110252419Sjulian} 110352419Sjulian 110452419Sjulian/* 110552419Sjulian * Call the appropriate message handler for the object. 110652419Sjulian * It is up to the message handler to free the message. 110752419Sjulian * If it's a generic message, handle it generically, otherwise 110852419Sjulian * call the type's message handler (if it exists) 110952419Sjulian */ 111052419Sjulian 111152419Sjulian#define CALL_MSG_HANDLER(error, node, msg, retaddr, resp) \ 111252419Sjuliando { \ 111352419Sjulian if((msg)->header.typecookie == NGM_GENERIC_COOKIE) { \ 111452419Sjulian (error) = ng_generic_msg((node), (msg), \ 111552419Sjulian (retaddr), (resp)); \ 111652419Sjulian } else { \ 111752419Sjulian if ((node)->type->rcvmsg != NULL) { \ 111852419Sjulian (error) = (*(node)->type->rcvmsg)((node), \ 111952419Sjulian (msg), (retaddr), (resp)); \ 112052419Sjulian } else { \ 112152419Sjulian TRAP_ERROR; \ 112252419Sjulian FREE((msg), M_NETGRAPH); \ 112352419Sjulian (error) = EINVAL; \ 112452419Sjulian } \ 112552419Sjulian } \ 112652419Sjulian} while (0) 112752419Sjulian 112852419Sjulian 112952419Sjulian/* 113052419Sjulian * Send a control message to a node 113152419Sjulian */ 113252419Sjulianint 113352419Sjulianng_send_msg(node_p here, struct ng_mesg *msg, const char *address, 113452419Sjulian struct ng_mesg **rptr) 113552419Sjulian{ 113652419Sjulian node_p dest = NULL; 113752419Sjulian char *retaddr = NULL; 113852419Sjulian int error; 113952419Sjulian 114052419Sjulian /* Find the target node */ 114152419Sjulian error = ng_path2node(here, address, &dest, &retaddr); 114252419Sjulian if (error) { 114352419Sjulian FREE(msg, M_NETGRAPH); 114452419Sjulian return error; 114552419Sjulian } 114652419Sjulian 114752419Sjulian /* Make sure the resp field is null before we start */ 114852419Sjulian if (rptr != NULL) 114952419Sjulian *rptr = NULL; 115052419Sjulian 115152419Sjulian CALL_MSG_HANDLER(error, dest, msg, retaddr, rptr); 115252419Sjulian 115352419Sjulian /* Make sure that if there is a response, it has the RESP bit set */ 115452419Sjulian if ((error == 0) && rptr && *rptr) 115552419Sjulian (*rptr)->header.flags |= NGF_RESP; 115652419Sjulian 115752419Sjulian /* 115852419Sjulian * If we had a return address it is up to us to free it. They should 115952419Sjulian * have taken a copy if they needed to make a delayed response. 116052419Sjulian */ 116152419Sjulian if (retaddr) 116252419Sjulian FREE(retaddr, M_NETGRAPH); 116352419Sjulian return (error); 116452419Sjulian} 116552419Sjulian 116652419Sjulian/* 116752419Sjulian * Implement the 'generic' control messages 116852419Sjulian */ 116952419Sjulianstatic int 117052419Sjulianng_generic_msg(node_p here, struct ng_mesg *msg, const char *retaddr, 117152419Sjulian struct ng_mesg **resp) 117252419Sjulian{ 117352419Sjulian int error = 0; 117452419Sjulian 117552419Sjulian if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 117652419Sjulian TRAP_ERROR; 117752419Sjulian FREE(msg, M_NETGRAPH); 117852419Sjulian return (EINVAL); 117952419Sjulian } 118052419Sjulian switch (msg->header.cmd) { 118152419Sjulian case NGM_SHUTDOWN: 118252419Sjulian ng_rmnode(here); 118352419Sjulian break; 118452419Sjulian case NGM_MKPEER: 118552419Sjulian { 118652419Sjulian struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 118752419Sjulian 118852419Sjulian if (msg->header.arglen != sizeof(*mkp)) { 118952419Sjulian TRAP_ERROR; 119052419Sjulian return (EINVAL); 119152419Sjulian } 119252419Sjulian mkp->type[sizeof(mkp->type) - 1] = '\0'; 119352419Sjulian mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 119452419Sjulian mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 119552419Sjulian error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 119652419Sjulian break; 119752419Sjulian } 119852419Sjulian case NGM_CONNECT: 119952419Sjulian { 120052419Sjulian struct ngm_connect *const con = 120152419Sjulian (struct ngm_connect *) msg->data; 120252419Sjulian node_p node2; 120352419Sjulian 120452419Sjulian if (msg->header.arglen != sizeof(*con)) { 120552419Sjulian TRAP_ERROR; 120652419Sjulian return (EINVAL); 120752419Sjulian } 120852419Sjulian con->path[sizeof(con->path) - 1] = '\0'; 120952419Sjulian con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 121052419Sjulian con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 121152419Sjulian error = ng_path2node(here, con->path, &node2, NULL); 121252419Sjulian if (error) 121352419Sjulian break; 121452419Sjulian error = ng_con_nodes(here, con->ourhook, node2, con->peerhook); 121552419Sjulian break; 121652419Sjulian } 121752419Sjulian case NGM_NAME: 121852419Sjulian { 121952419Sjulian struct ngm_name *const nam = (struct ngm_name *) msg->data; 122052419Sjulian 122152419Sjulian if (msg->header.arglen != sizeof(*nam)) { 122252419Sjulian TRAP_ERROR; 122352419Sjulian return (EINVAL); 122452419Sjulian } 122552419Sjulian nam->name[sizeof(nam->name) - 1] = '\0'; 122652419Sjulian error = ng_name_node(here, nam->name); 122752419Sjulian break; 122852419Sjulian } 122952419Sjulian case NGM_RMHOOK: 123052419Sjulian { 123152419Sjulian struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 123252419Sjulian hook_p hook; 123352419Sjulian 123452419Sjulian if (msg->header.arglen != sizeof(*rmh)) { 123552419Sjulian TRAP_ERROR; 123652419Sjulian return (EINVAL); 123752419Sjulian } 123852419Sjulian rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 123952419Sjulian LIST_FOREACH(hook, &here->hooks, hooks) { 124052419Sjulian if (hook->name && strcmp(hook->name, rmh->ourhook) == 0) 124152419Sjulian break; 124252419Sjulian } 124352419Sjulian if (hook) 124452419Sjulian ng_destroy_hook(hook); 124552419Sjulian break; 124652419Sjulian } 124752419Sjulian case NGM_NODEINFO: 124852419Sjulian { 124952419Sjulian struct nodeinfo *ni; 125052419Sjulian struct ng_mesg *rp; 125152419Sjulian 125252419Sjulian /* Get response struct */ 125352419Sjulian if (resp == NULL) { 125452419Sjulian error = EINVAL; 125552419Sjulian break; 125652419Sjulian } 125752419Sjulian NG_MKRESPONSE(rp, msg, sizeof(*ni), M_NOWAIT); 125852419Sjulian if (rp == NULL) { 125952419Sjulian error = ENOMEM; 126052419Sjulian break; 126152419Sjulian } 126252419Sjulian 126352419Sjulian /* Fill in node info */ 126452419Sjulian ni = (struct nodeinfo *) rp->data; 126552419Sjulian if (here->name != NULL) 126652419Sjulian strncpy(ni->name, here->name, NG_NODELEN); 126752419Sjulian strncpy(ni->type, here->type->name, NG_TYPELEN); 126852722Sjulian ni->id = ng_node2ID(here); 126952419Sjulian ni->hooks = here->numhooks; 127052419Sjulian *resp = rp; 127152419Sjulian break; 127252419Sjulian } 127352419Sjulian case NGM_LISTHOOKS: 127452419Sjulian { 127552419Sjulian const int nhooks = here->numhooks; 127652419Sjulian struct hooklist *hl; 127752419Sjulian struct nodeinfo *ni; 127852419Sjulian struct ng_mesg *rp; 127952419Sjulian hook_p hook; 128052419Sjulian 128152419Sjulian /* Get response struct */ 128252419Sjulian if (resp == NULL) { 128352419Sjulian error = EINVAL; 128452419Sjulian break; 128552419Sjulian } 128652419Sjulian NG_MKRESPONSE(rp, msg, sizeof(*hl) 128752419Sjulian + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 128852419Sjulian if (rp == NULL) { 128952419Sjulian error = ENOMEM; 129052419Sjulian break; 129152419Sjulian } 129252419Sjulian hl = (struct hooklist *) rp->data; 129352419Sjulian ni = &hl->nodeinfo; 129452419Sjulian 129552419Sjulian /* Fill in node info */ 129652419Sjulian if (here->name) 129752419Sjulian strncpy(ni->name, here->name, NG_NODELEN); 129852419Sjulian strncpy(ni->type, here->type->name, NG_TYPELEN); 129952722Sjulian ni->id = ng_node2ID(here); 130052419Sjulian 130152419Sjulian /* Cycle through the linked list of hooks */ 130252419Sjulian ni->hooks = 0; 130352419Sjulian LIST_FOREACH(hook, &here->hooks, hooks) { 130452419Sjulian struct linkinfo *const link = &hl->link[ni->hooks]; 130552419Sjulian 130652419Sjulian if (ni->hooks >= nhooks) { 130752419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 130852419Sjulian __FUNCTION__, "hooks"); 130952419Sjulian break; 131052419Sjulian } 131152419Sjulian if ((hook->flags & HK_INVALID) != 0) 131252419Sjulian continue; 131352419Sjulian strncpy(link->ourhook, hook->name, NG_HOOKLEN); 131452419Sjulian strncpy(link->peerhook, hook->peer->name, NG_HOOKLEN); 131552419Sjulian if (hook->peer->node->name != NULL) 131652419Sjulian strncpy(link->nodeinfo.name, 131752419Sjulian hook->peer->node->name, NG_NODELEN); 131852419Sjulian strncpy(link->nodeinfo.type, 131952419Sjulian hook->peer->node->type->name, NG_TYPELEN); 132052722Sjulian link->nodeinfo.id = ng_node2ID(hook->peer->node); 132152419Sjulian link->nodeinfo.hooks = hook->peer->node->numhooks; 132252419Sjulian ni->hooks++; 132352419Sjulian } 132452419Sjulian *resp = rp; 132552419Sjulian break; 132652419Sjulian } 132752419Sjulian 132852419Sjulian case NGM_LISTNAMES: 132952419Sjulian case NGM_LISTNODES: 133052419Sjulian { 133152419Sjulian const int unnamed = (msg->header.cmd == NGM_LISTNODES); 133252419Sjulian struct namelist *nl; 133352419Sjulian struct ng_mesg *rp; 133452419Sjulian node_p node; 133552419Sjulian int num = 0; 133652419Sjulian 133752419Sjulian if (resp == NULL) { 133852419Sjulian error = EINVAL; 133952419Sjulian break; 134052419Sjulian } 134152419Sjulian 134252419Sjulian /* Count number of nodes */ 134352419Sjulian LIST_FOREACH(node, &nodelist, nodes) { 134452419Sjulian if (unnamed || node->name != NULL) 134552419Sjulian num++; 134652419Sjulian } 134752419Sjulian 134852419Sjulian /* Get response struct */ 134952419Sjulian if (resp == NULL) { 135052419Sjulian error = EINVAL; 135152419Sjulian break; 135252419Sjulian } 135352419Sjulian NG_MKRESPONSE(rp, msg, sizeof(*nl) 135452419Sjulian + (num * sizeof(struct nodeinfo)), M_NOWAIT); 135552419Sjulian if (rp == NULL) { 135652419Sjulian error = ENOMEM; 135752419Sjulian break; 135852419Sjulian } 135952419Sjulian nl = (struct namelist *) rp->data; 136052419Sjulian 136152419Sjulian /* Cycle through the linked list of nodes */ 136252419Sjulian nl->numnames = 0; 136352419Sjulian LIST_FOREACH(node, &nodelist, nodes) { 136452419Sjulian struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; 136552419Sjulian 136652419Sjulian if (nl->numnames >= num) { 136752419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 136852419Sjulian __FUNCTION__, "nodes"); 136952419Sjulian break; 137052419Sjulian } 137152419Sjulian if ((node->flags & NG_INVALID) != 0) 137252419Sjulian continue; 137352419Sjulian if (!unnamed && node->name == NULL) 137452419Sjulian continue; 137552419Sjulian if (node->name != NULL) 137652419Sjulian strncpy(np->name, node->name, NG_NODELEN); 137752419Sjulian strncpy(np->type, node->type->name, NG_TYPELEN); 137852722Sjulian np->id = ng_node2ID(node); 137952419Sjulian np->hooks = node->numhooks; 138052419Sjulian nl->numnames++; 138152419Sjulian } 138252419Sjulian *resp = rp; 138352419Sjulian break; 138452419Sjulian } 138552419Sjulian 138652419Sjulian case NGM_LISTTYPES: 138752419Sjulian { 138852419Sjulian struct typelist *tl; 138952419Sjulian struct ng_mesg *rp; 139052419Sjulian struct ng_type *type; 139152419Sjulian int num = 0; 139252419Sjulian 139352419Sjulian if (resp == NULL) { 139452419Sjulian error = EINVAL; 139552419Sjulian break; 139652419Sjulian } 139752419Sjulian 139852419Sjulian /* Count number of types */ 139952419Sjulian LIST_FOREACH(type, &typelist, types) 140052419Sjulian num++; 140152419Sjulian 140252419Sjulian /* Get response struct */ 140352419Sjulian if (resp == NULL) { 140452419Sjulian error = EINVAL; 140552419Sjulian break; 140652419Sjulian } 140752419Sjulian NG_MKRESPONSE(rp, msg, sizeof(*tl) 140852419Sjulian + (num * sizeof(struct typeinfo)), M_NOWAIT); 140952419Sjulian if (rp == NULL) { 141052419Sjulian error = ENOMEM; 141152419Sjulian break; 141252419Sjulian } 141352419Sjulian tl = (struct typelist *) rp->data; 141452419Sjulian 141552419Sjulian /* Cycle through the linked list of types */ 141652419Sjulian tl->numtypes = 0; 141752419Sjulian LIST_FOREACH(type, &typelist, types) { 141852419Sjulian struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 141952419Sjulian 142052419Sjulian if (tl->numtypes >= num) { 142152419Sjulian log(LOG_ERR, "%s: number of %s changed\n", 142252419Sjulian __FUNCTION__, "types"); 142352419Sjulian break; 142452419Sjulian } 142552419Sjulian strncpy(tp->typename, type->name, NG_TYPELEN); 142652419Sjulian tp->numnodes = type->refs; 142752419Sjulian tl->numtypes++; 142852419Sjulian } 142952419Sjulian *resp = rp; 143052419Sjulian break; 143152419Sjulian } 143252419Sjulian 143353913Sarchie case NGM_BINARY2ASCII: 143453913Sarchie { 143553913Sarchie int bufSize = 2000; /* XXX hard coded constant */ 143653913Sarchie const struct ng_parse_type *argstype; 143753913Sarchie const struct ng_cmdlist *c; 143853913Sarchie struct ng_mesg *rp, *binary, *ascii; 143953913Sarchie 144053913Sarchie /* Data area must contain a valid netgraph message */ 144153913Sarchie binary = (struct ng_mesg *)msg->data; 144253913Sarchie if (msg->header.arglen < sizeof(struct ng_mesg) 144353913Sarchie || msg->header.arglen - sizeof(struct ng_mesg) 144453913Sarchie < binary->header.arglen) { 144553913Sarchie error = EINVAL; 144653913Sarchie break; 144753913Sarchie } 144853913Sarchie 144953913Sarchie /* Get a response message with lots of room */ 145053913Sarchie NG_MKRESPONSE(rp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 145153913Sarchie if (rp == NULL) { 145253913Sarchie error = ENOMEM; 145353913Sarchie break; 145453913Sarchie } 145553913Sarchie ascii = (struct ng_mesg *)rp->data; 145653913Sarchie 145753913Sarchie /* Copy binary message header to response message payload */ 145853913Sarchie bcopy(binary, ascii, sizeof(*binary)); 145953913Sarchie 146053913Sarchie /* Find command by matching typecookie and command number */ 146153913Sarchie for (c = here->type->cmdlist; 146253913Sarchie c != NULL && c->name != NULL; c++) { 146353913Sarchie if (binary->header.typecookie == c->cookie 146453913Sarchie && binary->header.cmd == c->cmd) 146553913Sarchie break; 146653913Sarchie } 146753913Sarchie if (c == NULL || c->name == NULL) { 146853913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 146953913Sarchie if (binary->header.typecookie == c->cookie 147053913Sarchie && binary->header.cmd == c->cmd) 147153913Sarchie break; 147253913Sarchie } 147353913Sarchie if (c->name == NULL) { 147453913Sarchie FREE(rp, M_NETGRAPH); 147553913Sarchie error = ENOSYS; 147653913Sarchie break; 147753913Sarchie } 147853913Sarchie } 147953913Sarchie 148053913Sarchie /* Convert command name to ASCII */ 148153913Sarchie snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 148253913Sarchie "%s", c->name); 148353913Sarchie 148453913Sarchie /* Convert command arguments to ASCII */ 148553913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 148653913Sarchie c->respType : c->mesgType; 148753913Sarchie if (argstype == NULL) 148853913Sarchie *ascii->data = '\0'; 148953913Sarchie else { 149053913Sarchie if ((error = ng_unparse(argstype, 149153913Sarchie (u_char *)binary->data, 149253913Sarchie ascii->data, bufSize)) != 0) { 149353913Sarchie FREE(rp, M_NETGRAPH); 149453913Sarchie break; 149553913Sarchie } 149653913Sarchie } 149753913Sarchie 149853913Sarchie /* Return the result as struct ng_mesg plus ASCII string */ 149953913Sarchie bufSize = strlen(ascii->data) + 1; 150053913Sarchie ascii->header.arglen = bufSize; 150153913Sarchie rp->header.arglen = sizeof(*ascii) + bufSize; 150253913Sarchie *resp = rp; 150353913Sarchie break; 150453913Sarchie } 150553913Sarchie 150653913Sarchie case NGM_ASCII2BINARY: 150753913Sarchie { 150853913Sarchie int bufSize = 2000; /* XXX hard coded constant */ 150953913Sarchie const struct ng_cmdlist *c; 151053913Sarchie const struct ng_parse_type *argstype; 151153913Sarchie struct ng_mesg *rp, *ascii, *binary; 151253913Sarchie int off; 151353913Sarchie 151453913Sarchie /* Data area must contain at least a struct ng_mesg + '\0' */ 151553913Sarchie ascii = (struct ng_mesg *)msg->data; 151653913Sarchie if (msg->header.arglen < sizeof(*ascii) + 1 151753913Sarchie || ascii->header.arglen < 1 151853913Sarchie || msg->header.arglen 151953913Sarchie < sizeof(*ascii) + ascii->header.arglen) { 152053913Sarchie error = EINVAL; 152153913Sarchie break; 152253913Sarchie } 152353913Sarchie ascii->data[ascii->header.arglen - 1] = '\0'; 152453913Sarchie 152553913Sarchie /* Get a response message with lots of room */ 152653913Sarchie NG_MKRESPONSE(rp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 152753913Sarchie if (rp == NULL) { 152853913Sarchie error = ENOMEM; 152953913Sarchie break; 153053913Sarchie } 153153913Sarchie binary = (struct ng_mesg *)rp->data; 153253913Sarchie 153353913Sarchie /* Copy ASCII message header to response message payload */ 153453913Sarchie bcopy(ascii, binary, sizeof(*ascii)); 153553913Sarchie 153653913Sarchie /* Find command by matching ASCII command string */ 153753913Sarchie for (c = here->type->cmdlist; 153853913Sarchie c != NULL && c->name != NULL; c++) { 153953913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 154053913Sarchie break; 154153913Sarchie } 154253913Sarchie if (c == NULL || c->name == NULL) { 154353913Sarchie for (c = ng_generic_cmds; c->name != NULL; c++) { 154453913Sarchie if (strcmp(ascii->header.cmdstr, c->name) == 0) 154553913Sarchie break; 154653913Sarchie } 154753913Sarchie if (c->name == NULL) { 154853913Sarchie FREE(rp, M_NETGRAPH); 154953913Sarchie error = ENOSYS; 155053913Sarchie break; 155153913Sarchie } 155253913Sarchie } 155353913Sarchie 155453913Sarchie /* Convert command name to binary */ 155553913Sarchie binary->header.cmd = c->cmd; 155653913Sarchie binary->header.typecookie = c->cookie; 155753913Sarchie 155853913Sarchie /* Convert command arguments to binary */ 155953913Sarchie argstype = (binary->header.flags & NGF_RESP) ? 156053913Sarchie c->respType : c->mesgType; 156153913Sarchie if (argstype == NULL) 156253913Sarchie bufSize = 0; 156353913Sarchie else { 156453913Sarchie if ((error = ng_parse(argstype, ascii->data, 156553913Sarchie &off, (u_char *)binary->data, &bufSize)) != 0) { 156653913Sarchie FREE(rp, M_NETGRAPH); 156753913Sarchie break; 156853913Sarchie } 156953913Sarchie } 157053913Sarchie 157153913Sarchie /* Return the result */ 157253913Sarchie binary->header.arglen = bufSize; 157353913Sarchie rp->header.arglen = sizeof(*binary) + bufSize; 157453913Sarchie *resp = rp; 157553913Sarchie break; 157653913Sarchie } 157753913Sarchie 157852419Sjulian case NGM_TEXT_STATUS: 157952419Sjulian /* 158052419Sjulian * This one is tricky as it passes the command down to the 158152419Sjulian * actual node, even though it is a generic type command. 158252419Sjulian * This means we must assume that the msg is already freed 158352419Sjulian * when control passes back to us. 158452419Sjulian */ 158552419Sjulian if (resp == NULL) { 158652419Sjulian error = EINVAL; 158752419Sjulian break; 158852419Sjulian } 158952419Sjulian if (here->type->rcvmsg != NULL) 159052419Sjulian return((*here->type->rcvmsg)(here, msg, retaddr, resp)); 159152419Sjulian /* Fall through if rcvmsg not supported */ 159252419Sjulian default: 159352419Sjulian TRAP_ERROR; 159452419Sjulian error = EINVAL; 159552419Sjulian } 159652419Sjulian FREE(msg, M_NETGRAPH); 159752419Sjulian return (error); 159852419Sjulian} 159952419Sjulian 160052419Sjulian/* 160152419Sjulian * Send a data packet to a node. If the recipient has no 160252419Sjulian * 'receive data' method, then silently discard the packet. 160352419Sjulian */ 160452419Sjulianint 160552419Sjulianng_send_data(hook_p hook, struct mbuf *m, meta_p meta) 160652419Sjulian{ 160752419Sjulian int (*rcvdata)(hook_p, struct mbuf *, meta_p); 160852419Sjulian int error; 160952419Sjulian 161053403Sarchie CHECK_DATA_MBUF(m); 161152419Sjulian if (hook && (hook->flags & HK_INVALID) == 0) { 161252419Sjulian rcvdata = hook->peer->node->type->rcvdata; 161352419Sjulian if (rcvdata != NULL) 161452419Sjulian error = (*rcvdata)(hook->peer, m, meta); 161552419Sjulian else { 161652419Sjulian error = 0; 161752419Sjulian NG_FREE_DATA(m, meta); 161852419Sjulian } 161952419Sjulian } else { 162052419Sjulian TRAP_ERROR; 162152419Sjulian error = ENOTCONN; 162252419Sjulian NG_FREE_DATA(m, meta); 162352419Sjulian } 162452419Sjulian return (error); 162552419Sjulian} 162652419Sjulian 162752419Sjulian/* 162852419Sjulian * Send a queued data packet to a node. If the recipient has no 162952419Sjulian * 'receive queued data' method, then try the 'receive data' method above. 163052419Sjulian */ 163152419Sjulianint 163252419Sjulianng_send_dataq(hook_p hook, struct mbuf *m, meta_p meta) 163352419Sjulian{ 163452419Sjulian int (*rcvdataq)(hook_p, struct mbuf *, meta_p); 163552419Sjulian int error; 163652419Sjulian 163753403Sarchie CHECK_DATA_MBUF(m); 163852419Sjulian if (hook && (hook->flags & HK_INVALID) == 0) { 163952419Sjulian rcvdataq = hook->peer->node->type->rcvdataq; 164052419Sjulian if (rcvdataq != NULL) 164152419Sjulian error = (*rcvdataq)(hook->peer, m, meta); 164252419Sjulian else { 164352419Sjulian error = ng_send_data(hook, m, meta); 164452419Sjulian } 164552419Sjulian } else { 164652419Sjulian TRAP_ERROR; 164752419Sjulian error = ENOTCONN; 164852419Sjulian NG_FREE_DATA(m, meta); 164952419Sjulian } 165052419Sjulian return (error); 165152419Sjulian} 165252419Sjulian 165352419Sjulian/************************************************************************ 165452419Sjulian Module routines 165552419Sjulian************************************************************************/ 165652419Sjulian 165752419Sjulian/* 165852419Sjulian * Handle the loading/unloading of a netgraph node type module 165952419Sjulian */ 166052419Sjulianint 166152419Sjulianng_mod_event(module_t mod, int event, void *data) 166252419Sjulian{ 166352419Sjulian struct ng_type *const type = data; 166452419Sjulian int s, error = 0; 166552419Sjulian 166652419Sjulian switch (event) { 166752419Sjulian case MOD_LOAD: 166852419Sjulian 166952419Sjulian /* Register new netgraph node type */ 167052419Sjulian s = splnet(); 167152419Sjulian if ((error = ng_newtype(type)) != 0) { 167252419Sjulian splx(s); 167352419Sjulian break; 167452419Sjulian } 167552419Sjulian 167652419Sjulian /* Call type specific code */ 167752419Sjulian if (type->mod_event != NULL) 167852419Sjulian if ((error = (*type->mod_event)(mod, event, data)) != 0) 167952419Sjulian LIST_REMOVE(type, types); 168052419Sjulian splx(s); 168152419Sjulian break; 168252419Sjulian 168352419Sjulian case MOD_UNLOAD: 168452419Sjulian s = splnet(); 168552419Sjulian if (type->refs != 0) /* make sure no nodes exist! */ 168652419Sjulian error = EBUSY; 168752419Sjulian else { 168852419Sjulian if (type->mod_event != NULL) { /* check with type */ 168952419Sjulian error = (*type->mod_event)(mod, event, data); 169052419Sjulian if (error != 0) { /* type refuses.. */ 169152419Sjulian splx(s); 169252419Sjulian break; 169352419Sjulian } 169452419Sjulian } 169552419Sjulian LIST_REMOVE(type, types); 169652419Sjulian } 169752419Sjulian splx(s); 169852419Sjulian break; 169952419Sjulian 170052419Sjulian default: 170152419Sjulian if (type->mod_event != NULL) 170252419Sjulian error = (*type->mod_event)(mod, event, data); 170352419Sjulian else 170452419Sjulian error = 0; /* XXX ? */ 170552419Sjulian break; 170652419Sjulian } 170752419Sjulian return (error); 170852419Sjulian} 170952419Sjulian 171052419Sjulian/* 171152419Sjulian * Handle loading and unloading for this code. 171252419Sjulian * The only thing we need to link into is the NETISR strucure. 171352419Sjulian */ 171452419Sjulianstatic int 171552419Sjulianngb_mod_event(module_t mod, int event, void *data) 171652419Sjulian{ 171752419Sjulian int s, error = 0; 171852419Sjulian 171952419Sjulian switch (event) { 172052419Sjulian case MOD_LOAD: 172152419Sjulian /* Register line discipline */ 172252419Sjulian s = splimp(); 172352419Sjulian error = register_netisr(NETISR_NETGRAPH, ngintr); 172452419Sjulian splx(s); 172552419Sjulian break; 172652419Sjulian case MOD_UNLOAD: 172752419Sjulian /* You cant unload it because an interface may be using it. */ 172852419Sjulian error = EBUSY; 172952419Sjulian break; 173052419Sjulian default: 173152419Sjulian error = EOPNOTSUPP; 173252419Sjulian break; 173352419Sjulian } 173452419Sjulian return (error); 173552419Sjulian} 173652419Sjulian 173752419Sjulianstatic moduledata_t netgraph_mod = { 173852419Sjulian "netgraph", 173952419Sjulian ngb_mod_event, 174052419Sjulian (NULL) 174152419Sjulian}; 174252419SjulianDECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 174352419Sjulian 174452419Sjulian/************************************************************************ 174552419Sjulian Queueing routines 174652419Sjulian************************************************************************/ 174752419Sjulian 174852419Sjulian/* The structure for queueing across ISR switches */ 174952419Sjulianstruct ng_queue_entry { 175052419Sjulian u_long flags; 175152419Sjulian struct ng_queue_entry *next; 175252419Sjulian union { 175352419Sjulian struct { 175452419Sjulian hook_p da_hook; /* target hook */ 175552419Sjulian struct mbuf *da_m; 175652419Sjulian meta_p da_meta; 175752419Sjulian } data; 175852419Sjulian struct { 175952419Sjulian struct ng_mesg *msg_msg; 176052419Sjulian node_p msg_node; 176152419Sjulian void *msg_retaddr; 176252419Sjulian } msg; 176352419Sjulian } body; 176452419Sjulian}; 176552419Sjulian#define NGQF_DATA 0x01 /* the queue element is data */ 176652419Sjulian#define NGQF_MESG 0x02 /* the queue element is a message */ 176752419Sjulian 176852419Sjulianstatic struct ng_queue_entry *ngqbase; /* items to be unqueued */ 176952419Sjulianstatic struct ng_queue_entry *ngqlast; /* last item queued */ 177052419Sjulianstatic const int ngqroom = 64; /* max items to queue */ 177152419Sjulianstatic int ngqsize; /* number of items in queue */ 177252419Sjulian 177352419Sjulianstatic struct ng_queue_entry *ngqfree; /* free ones */ 177452419Sjulianstatic const int ngqfreemax = 16;/* cache at most this many */ 177552419Sjulianstatic int ngqfreesize; /* number of cached entries */ 177652419Sjulian 177752419Sjulian/* 177852419Sjulian * Get a queue entry 177952419Sjulian */ 178052419Sjulianstatic struct ng_queue_entry * 178152419Sjulianng_getqblk(void) 178252419Sjulian{ 178352419Sjulian register struct ng_queue_entry *q; 178452419Sjulian int s; 178552419Sjulian 178652419Sjulian /* Could be guarding against tty ints or whatever */ 178752419Sjulian s = splhigh(); 178852419Sjulian 178952419Sjulian /* Try get a cached queue block, or else allocate a new one */ 179052419Sjulian if ((q = ngqfree) == NULL) { 179152419Sjulian splx(s); 179252419Sjulian if (ngqsize < ngqroom) { /* don't worry about races */ 179352419Sjulian MALLOC(q, struct ng_queue_entry *, 179452419Sjulian sizeof(*q), M_NETGRAPH, M_NOWAIT); 179552419Sjulian } 179652419Sjulian } else { 179752419Sjulian ngqfree = q->next; 179852419Sjulian ngqfreesize--; 179952419Sjulian splx(s); 180052419Sjulian } 180152419Sjulian return (q); 180252419Sjulian} 180352419Sjulian 180452419Sjulian/* 180552419Sjulian * Release a queue entry 180652419Sjulian */ 180752419Sjulian#define RETURN_QBLK(q) \ 180852419Sjuliando { \ 180952419Sjulian int s; \ 181052419Sjulian if (ngqfreesize < ngqfreemax) { /* don't worry about races */ \ 181152419Sjulian s = splhigh(); \ 181252419Sjulian (q)->next = ngqfree; \ 181352419Sjulian ngqfree = (q); \ 181452419Sjulian ngqfreesize++; \ 181552419Sjulian splx(s); \ 181652419Sjulian } else { \ 181752419Sjulian FREE((q), M_NETGRAPH); \ 181852419Sjulian } \ 181952419Sjulian} while (0) 182052419Sjulian 182152419Sjulian/* 182252419Sjulian * Running at a raised (but we don't know which) processor priority level, 182352419Sjulian * put the data onto a queue to be picked up by another PPL (probably splnet) 182452419Sjulian */ 182552419Sjulianint 182652419Sjulianng_queue_data(hook_p hook, struct mbuf *m, meta_p meta) 182752419Sjulian{ 182852419Sjulian struct ng_queue_entry *q; 182952419Sjulian int s; 183052419Sjulian 183152419Sjulian if (hook == NULL) { 183252419Sjulian NG_FREE_DATA(m, meta); 183352419Sjulian return (0); 183452419Sjulian } 183552419Sjulian if ((q = ng_getqblk()) == NULL) { 183652419Sjulian NG_FREE_DATA(m, meta); 183752419Sjulian return (ENOBUFS); 183852419Sjulian } 183952419Sjulian 184052419Sjulian /* Fill out the contents */ 184152419Sjulian q->flags = NGQF_DATA; 184252419Sjulian q->next = NULL; 184352419Sjulian q->body.data.da_hook = hook; 184452419Sjulian q->body.data.da_m = m; 184552419Sjulian q->body.data.da_meta = meta; 184652419Sjulian hook->refs++; /* don't let it go away while on the queue */ 184752419Sjulian 184852419Sjulian /* Put it on the queue */ 184952419Sjulian s = splhigh(); 185052419Sjulian if (ngqbase) { 185152419Sjulian ngqlast->next = q; 185252419Sjulian } else { 185352419Sjulian ngqbase = q; 185452419Sjulian } 185552419Sjulian ngqlast = q; 185652419Sjulian ngqsize++; 185752419Sjulian splx(s); 185852419Sjulian 185952419Sjulian /* Schedule software interrupt to handle it later */ 186052419Sjulian schednetisr(NETISR_NETGRAPH); 186152419Sjulian return (0); 186252419Sjulian} 186352419Sjulian 186452419Sjulian/* 186552419Sjulian * Running at a raised (but we don't know which) processor priority level, 186652419Sjulian * put the msg onto a queue to be picked up by another PPL (probably splnet) 186752419Sjulian */ 186852419Sjulianint 186952419Sjulianng_queue_msg(node_p here, struct ng_mesg * msg, int len, const char *address) 187052419Sjulian{ 187152419Sjulian register struct ng_queue_entry *q; 187252419Sjulian int s; 187352419Sjulian node_p dest = NULL; 187452419Sjulian char *retaddr = NULL; 187552419Sjulian int error; 187652419Sjulian 187752722Sjulian /* Find the target node. */ 187852419Sjulian error = ng_path2node(here, address, &dest, &retaddr); 187952419Sjulian if (error) { 188052419Sjulian FREE(msg, M_NETGRAPH); 188152419Sjulian return (error); 188252419Sjulian } 188352419Sjulian if ((q = ng_getqblk()) == NULL) { 188452419Sjulian FREE(msg, M_NETGRAPH); 188552419Sjulian if (retaddr) 188652419Sjulian FREE(retaddr, M_NETGRAPH); 188752419Sjulian return (ENOBUFS); 188852419Sjulian } 188952419Sjulian 189052419Sjulian /* Fill out the contents */ 189152419Sjulian q->flags = NGQF_MESG; 189252419Sjulian q->next = NULL; 189352419Sjulian q->body.msg.msg_node = dest; 189452419Sjulian q->body.msg.msg_msg = msg; 189552419Sjulian q->body.msg.msg_retaddr = retaddr; 189652419Sjulian dest->refs++; /* don't let it go away while on the queue */ 189752419Sjulian 189852419Sjulian /* Put it on the queue */ 189952419Sjulian s = splhigh(); 190052419Sjulian if (ngqbase) { 190152419Sjulian ngqlast->next = q; 190252419Sjulian } else { 190352419Sjulian ngqbase = q; 190452419Sjulian } 190552419Sjulian ngqlast = q; 190652419Sjulian ngqsize++; 190752419Sjulian splx(s); 190852419Sjulian 190952419Sjulian /* Schedule software interrupt to handle it later */ 191052419Sjulian schednetisr(NETISR_NETGRAPH); 191152419Sjulian return (0); 191252419Sjulian} 191352419Sjulian 191452419Sjulian/* 191552419Sjulian * Pick an item off the queue, process it, and dispose of the queue entry. 191652419Sjulian * Should be running at splnet. 191752419Sjulian */ 191852419Sjulianstatic void 191952419Sjulianngintr(void) 192052419Sjulian{ 192152419Sjulian hook_p hook; 192252419Sjulian struct ng_queue_entry *ngq; 192352419Sjulian struct mbuf *m; 192452419Sjulian meta_p meta; 192552419Sjulian void *retaddr; 192652419Sjulian struct ng_mesg *msg; 192752419Sjulian node_p node; 192852419Sjulian int error = 0; 192952419Sjulian int s; 193052419Sjulian 193152419Sjulian while (1) { 193252419Sjulian s = splhigh(); 193352419Sjulian if ((ngq = ngqbase)) { 193452419Sjulian ngqbase = ngq->next; 193552419Sjulian ngqsize--; 193652419Sjulian } 193752419Sjulian splx(s); 193852419Sjulian if (ngq == NULL) 193952419Sjulian return; 194052419Sjulian switch (ngq->flags) { 194152419Sjulian case NGQF_DATA: 194252419Sjulian hook = ngq->body.data.da_hook; 194352419Sjulian m = ngq->body.data.da_m; 194452419Sjulian meta = ngq->body.data.da_meta; 194552419Sjulian RETURN_QBLK(ngq); 194652419Sjulian NG_SEND_DATAQ(error, hook, m, meta); 194752419Sjulian ng_unref_hook(hook); 194852419Sjulian break; 194952419Sjulian case NGQF_MESG: 195052419Sjulian node = ngq->body.msg.msg_node; 195152419Sjulian msg = ngq->body.msg.msg_msg; 195252419Sjulian retaddr = ngq->body.msg.msg_retaddr; 195352419Sjulian RETURN_QBLK(ngq); 195452419Sjulian if (node->flags & NG_INVALID) { 195552419Sjulian FREE(msg, M_NETGRAPH); 195652419Sjulian } else { 195752419Sjulian CALL_MSG_HANDLER(error, node, msg, 195852419Sjulian retaddr, NULL); 195952419Sjulian } 196052419Sjulian ng_unref(node); 196152419Sjulian if (retaddr) 196252419Sjulian FREE(retaddr, M_NETGRAPH); 196352419Sjulian break; 196452419Sjulian default: 196552419Sjulian RETURN_QBLK(ngq); 196652419Sjulian } 196752419Sjulian } 196852419Sjulian} 196952419Sjulian 197052419Sjulian 1971