ng_sample.c revision 69922
152419Sjulian 252419Sjulian/* 352419Sjulian * ng_sample.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 * Author: Julian Elischer <julian@freebsd.org> 3852419Sjulian * 3952419Sjulian * $FreeBSD: head/sys/netgraph/ng_sample.c 69922 2000-12-12 18:52:14Z julian $ 4052752Sjulian * $Whistle: ng_sample.c,v 1.13 1999/11/01 09:24:52 julian Exp $ 4152419Sjulian */ 4252419Sjulian 4352419Sjulian#include <sys/param.h> 4452419Sjulian#include <sys/systm.h> 4552419Sjulian#include <sys/kernel.h> 4652419Sjulian#include <sys/mbuf.h> 4752419Sjulian#include <sys/malloc.h> 4853997Sarchie#include <sys/ctype.h> 4952419Sjulian#include <sys/errno.h> 5052419Sjulian#include <sys/syslog.h> 5152419Sjulian 5252419Sjulian#include <netgraph/ng_message.h> 5353997Sarchie#include <netgraph/ng_parse.h> 5452419Sjulian#include <netgraph/ng_sample.h> 5552419Sjulian#include <netgraph/netgraph.h> 5652419Sjulian 5752419Sjulian/* 5852419Sjulian * This section contains the netgraph method declarations for the 5952419Sjulian * sample node. These methods define the netgraph 'type'. 6052419Sjulian */ 6152419Sjulian 6252752Sjulianstatic ng_constructor_t ng_xxx_constructor; 6352752Sjulianstatic ng_rcvmsg_t ng_xxx_rcvmsg; 6452752Sjulianstatic ng_shutdown_t ng_xxx_rmnode; 6552752Sjulianstatic ng_newhook_t ng_xxx_newhook; 6652752Sjulianstatic ng_connect_t ng_xxx_connect; 6752752Sjulianstatic ng_rcvdata_t ng_xxx_rcvdata; /* note these are both ng_rcvdata_t */ 6852752Sjulianstatic ng_disconnect_t ng_xxx_disconnect; 6952419Sjulian 7053997Sarchie/* Parse type for struct ngxxxstat */ 7153997Sarchiestatic const struct ng_parse_struct_info 7253997Sarchie ng_xxx_stat_type_info = NG_XXX_STATS_TYPE_INFO; 7353997Sarchiestatic const struct ng_parse_type ng_xxx_stat_type = { 7453997Sarchie &ng_parse_struct_type, 7553997Sarchie &ng_xxx_stat_type_info 7653997Sarchie}; 7753997Sarchie 7853997Sarchie/* List of commands and how to convert arguments to/from ASCII */ 7953997Sarchiestatic const struct ng_cmdlist ng_xxx_cmdlist[] = { 8053997Sarchie { 8153997Sarchie NGM_XXX_COOKIE, 8253997Sarchie NGM_XXX_GET_STATUS, 8353997Sarchie "getstatus", 8453997Sarchie NULL, 8553997Sarchie &ng_xxx_stat_type, 8653997Sarchie }, 8753997Sarchie { 8853997Sarchie NGM_XXX_COOKIE, 8953997Sarchie NGM_XXX_SET_FLAG, 9053997Sarchie "setflag", 9153997Sarchie &ng_parse_int32_type, 9253997Sarchie NULL 9353997Sarchie }, 9453997Sarchie { 0 } 9553997Sarchie}; 9653997Sarchie 9752419Sjulian/* Netgraph node type descriptor */ 9852419Sjulianstatic struct ng_type typestruct = { 9952419Sjulian NG_VERSION, 10052419Sjulian NG_XXX_NODE_TYPE, 10152419Sjulian NULL, 10252419Sjulian ng_xxx_constructor, 10352419Sjulian ng_xxx_rcvmsg, 10452419Sjulian ng_xxx_rmnode, 10552419Sjulian ng_xxx_newhook, 10652419Sjulian NULL, 10752419Sjulian ng_xxx_connect, 10852419Sjulian ng_xxx_rcvdata, 10953913Sarchie ng_xxx_disconnect, 11053997Sarchie ng_xxx_cmdlist 11152419Sjulian}; 11252419SjulianNETGRAPH_INIT(xxx, &typestruct); 11352419Sjulian 11452419Sjulian/* Information we store for each hook on each node */ 11552419Sjulianstruct XXX_hookinfo { 11652419Sjulian int dlci; /* The DLCI it represents, -1 == downstream */ 11752419Sjulian int channel; /* The channel representing this DLCI */ 11852419Sjulian hook_p hook; 11952419Sjulian}; 12052419Sjulian 12152419Sjulian/* Information we store for each node */ 12252419Sjulianstruct XXX { 12352419Sjulian struct XXX_hookinfo channel[XXX_NUM_DLCIS]; 12452419Sjulian struct XXX_hookinfo downstream_hook; 12552419Sjulian node_p node; /* back pointer to node */ 12652419Sjulian hook_p debughook; 12752419Sjulian u_int packets_in; /* packets in from downstream */ 12852419Sjulian u_int packets_out; /* packets out towards downstream */ 12952419Sjulian u_int32_t flags; 13052419Sjulian}; 13152419Sjuliantypedef struct XXX *xxx_p; 13252419Sjulian 13352419Sjulian/* 13452419Sjulian * Allocate the private data structure and the generic node 13552419Sjulian * and link them together. 13652419Sjulian * 13752419Sjulian * ng_make_node_common() returns with a generic node struct 13852419Sjulian * with a single reference for us.. we transfer it to the 13952419Sjulian * private structure.. when we free the private struct we must 14052419Sjulian * unref the node so it gets freed too. 14152419Sjulian * 14252419Sjulian * If this were a device node than this work would be done in the attach() 14352419Sjulian * routine and the constructor would return EINVAL as you should not be able 14452419Sjulian * to creatednodes that depend on hardware (unless you can add the hardware :) 14552419Sjulian */ 14652419Sjulianstatic int 14752419Sjulianng_xxx_constructor(node_p *nodep) 14852419Sjulian{ 14952419Sjulian xxx_p privdata; 15052419Sjulian int i, error; 15152419Sjulian 15252419Sjulian /* Initialize private descriptor */ 15368876Sdwmalone MALLOC(privdata, xxx_p, sizeof(*privdata), M_NETGRAPH, 15468876Sdwmalone M_NOWAIT | M_ZERO); 15552419Sjulian if (privdata == NULL) 15652419Sjulian return (ENOMEM); 15752419Sjulian for (i = 0; i < XXX_NUM_DLCIS; i++) { 15852419Sjulian privdata->channel[i].dlci = -2; 15952419Sjulian privdata->channel[i].channel = i; 16052419Sjulian } 16152419Sjulian 16252419Sjulian /* Call the 'generic' (ie, superclass) node constructor */ 16352419Sjulian if ((error = ng_make_node_common(&typestruct, nodep))) { 16452419Sjulian FREE(privdata, M_NETGRAPH); 16552419Sjulian return (error); 16652419Sjulian } 16752419Sjulian 16852419Sjulian /* Link structs together; this counts as our one reference to *nodep */ 16952419Sjulian (*nodep)->private = privdata; 17052419Sjulian privdata->node = *nodep; 17152419Sjulian return (0); 17252419Sjulian} 17352419Sjulian 17452419Sjulian/* 17552419Sjulian * Give our ok for a hook to be added... 17652419Sjulian * If we are not running this might kick a device into life. 17752419Sjulian * Possibly decode information out of the hook name. 17852419Sjulian * Add the hook's private info to the hook structure. 17952419Sjulian * (if we had some). In this example, we assume that there is a 18052419Sjulian * an array of structs, called 'channel' in the private info, 18152419Sjulian * one for each active channel. The private 18252419Sjulian * pointer of each hook points to the appropriate XXX_hookinfo struct 18352419Sjulian * so that the source of an input packet is easily identified. 18452419Sjulian * (a dlci is a frame relay channel) 18552419Sjulian */ 18652419Sjulianstatic int 18752419Sjulianng_xxx_newhook(node_p node, hook_p hook, const char *name) 18852419Sjulian{ 18952419Sjulian const xxx_p xxxp = node->private; 19052419Sjulian const char *cp; 19152419Sjulian int dlci = 0; 19252419Sjulian int chan; 19352419Sjulian 19452419Sjulian#if 0 19552419Sjulian /* Possibly start up the device if it's not already going */ 19652419Sjulian if ((xxxp->flags & SCF_RUNNING) == 0) { 19752419Sjulian ng_xxx_start_hardware(xxxp); 19852419Sjulian } 19952419Sjulian#endif 20052419Sjulian 20152419Sjulian /* Example of how one might use hooks with embedded numbers: All 20252419Sjulian * hooks start with 'dlci' and have a decimal trailing channel 20352419Sjulian * number up to 4 digits Use the leadin defined int he associated .h 20452419Sjulian * file. */ 20552816Sarchie if (strncmp(name, 20652816Sarchie NG_XXX_HOOK_DLCI_LEADIN, strlen(NG_XXX_HOOK_DLCI_LEADIN)) == 0) { 20753648Sarchie char *eptr; 20852816Sarchie 20952419Sjulian cp = name + sizeof(NG_XXX_HOOK_DLCI_LEADIN); 21052816Sarchie if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) 21152419Sjulian return (EINVAL); 21252816Sarchie dlci = (int)strtoul(cp, &eptr, 10); 21352816Sarchie if (*eptr != '\0' || dlci < 0 || dlci > 1023) 21452816Sarchie return (EINVAL); 21552816Sarchie 21652419Sjulian /* We have a dlci, now either find it, or allocate it */ 21752419Sjulian for (chan = 0; chan < XXX_NUM_DLCIS; chan++) 21852419Sjulian if (xxxp->channel[chan].dlci == dlci) 21952419Sjulian break; 22052419Sjulian if (chan == XXX_NUM_DLCIS) { 22152419Sjulian for (chan = 0; chan < XXX_NUM_DLCIS; chan++) 22252419Sjulian if (xxxp->channel[chan].dlci != -2) 22352419Sjulian continue; 22452419Sjulian if (chan == XXX_NUM_DLCIS) 22552419Sjulian return (ENOBUFS); 22652419Sjulian } 22752419Sjulian if (xxxp->channel[chan].hook != NULL) 22852419Sjulian return (EADDRINUSE); 22952419Sjulian hook->private = xxxp->channel + chan; 23052419Sjulian xxxp->channel[chan].hook = hook; 23152419Sjulian return (0); 23252419Sjulian } else if (strcmp(name, NG_XXX_HOOK_DOWNSTREAM) == 0) { 23352419Sjulian /* Example of simple predefined hooks. */ 23452419Sjulian /* do something specific to the downstream connection */ 23552419Sjulian xxxp->downstream_hook.hook = hook; 23652419Sjulian hook->private = &xxxp->downstream_hook; 23752419Sjulian } else if (strcmp(name, NG_XXX_HOOK_DEBUG) == 0) { 23852419Sjulian /* do something specific to a debug connection */ 23952419Sjulian xxxp->debughook = hook; 24052419Sjulian hook->private = NULL; 24152419Sjulian } else 24252419Sjulian return (EINVAL); /* not a hook we know about */ 24352419Sjulian return(0); 24452419Sjulian} 24552419Sjulian 24652419Sjulian/* 24752419Sjulian * Get a netgraph control message. 24852419Sjulian * Check it is one we understand. If needed, send a response. 24952419Sjulian * We could save the address for an async action later, but don't here. 25052419Sjulian * Always free the message. 25152419Sjulian * The response should be in a malloc'd region that the caller can 'free'. 25252419Sjulian * A response is not required. 25352419Sjulian * Theoretically you could respond defferently to old message types if 25452419Sjulian * the cookie in the header didn't match what we consider to be current 25552419Sjulian * (so that old userland programs could continue to work). 25652419Sjulian */ 25752419Sjulianstatic int 25859728Sjulianng_xxx_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr, 25959728Sjulian struct ng_mesg **rptr, hook_p lasthook) 26052419Sjulian{ 26152419Sjulian const xxx_p xxxp = node->private; 26252419Sjulian struct ng_mesg *resp = NULL; 26352419Sjulian int error = 0; 26452419Sjulian 26552419Sjulian /* Deal with message according to cookie and command */ 26652419Sjulian switch (msg->header.typecookie) { 26752419Sjulian case NGM_XXX_COOKIE: 26852419Sjulian switch (msg->header.cmd) { 26952419Sjulian case NGM_XXX_GET_STATUS: 27052419Sjulian { 27152419Sjulian struct ngxxxstat *stats; 27252419Sjulian 27352419Sjulian NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 27452419Sjulian if (!resp) { 27552419Sjulian error = ENOMEM; 27652419Sjulian break; 27752419Sjulian } 27852419Sjulian stats = (struct ngxxxstat *) resp->data; 27952419Sjulian stats->packets_in = xxxp->packets_in; 28052419Sjulian stats->packets_out = xxxp->packets_out; 28152419Sjulian break; 28252419Sjulian } 28352419Sjulian case NGM_XXX_SET_FLAG: 28452419Sjulian if (msg->header.arglen != sizeof(u_int32_t)) { 28552419Sjulian error = EINVAL; 28652419Sjulian break; 28752419Sjulian } 28852419Sjulian xxxp->flags = *((u_int32_t *) msg->data); 28952419Sjulian break; 29052419Sjulian default: 29152419Sjulian error = EINVAL; /* unknown command */ 29252419Sjulian break; 29352419Sjulian } 29452419Sjulian break; 29552419Sjulian default: 29652419Sjulian error = EINVAL; /* unknown cookie type */ 29752419Sjulian break; 29852419Sjulian } 29952419Sjulian 30052419Sjulian /* Take care of synchronous response, if any */ 30152419Sjulian if (rptr) 30252419Sjulian *rptr = resp; 30352419Sjulian else if (resp) 30452419Sjulian FREE(resp, M_NETGRAPH); 30552419Sjulian 30652419Sjulian /* Free the message and return */ 30752419Sjulian FREE(msg, M_NETGRAPH); 30852419Sjulian return(error); 30952419Sjulian} 31052419Sjulian 31152419Sjulian/* 31252419Sjulian * Receive data, and do something with it. 31352419Sjulian * Possibly send it out on another link after processing. 31452419Sjulian * Possibly do something different if it comes from different 31552419Sjulian * hooks. the caller will never free m or meta, so 31652419Sjulian * if we use up this data or abort we must free BOTH of these. 31752419Sjulian * 31852419Sjulian * If we want, we may decide to force this data to be queued and reprocessed 31969922Sjulian * at the netgraph NETISR time. 32069922Sjulian * We would do that by setting the HK_QUEUE flag on our hook. We would do that 32169922Sjulian * in the connect() method. 32252419Sjulian */ 32352419Sjulianstatic int 32459728Sjulianng_xxx_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, 32569922Sjulian struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) 32652419Sjulian{ 32752419Sjulian const xxx_p xxxp = hook->node->private; 32852419Sjulian int chan = -2; 32952419Sjulian int dlci = -2; 33052419Sjulian int error; 33152419Sjulian 33252419Sjulian if (hook->private) { 33352419Sjulian dlci = ((struct XXX_hookinfo *) hook->private)->dlci; 33452419Sjulian chan = ((struct XXX_hookinfo *) hook->private)->channel; 33552419Sjulian if (dlci != -1) { 33652419Sjulian /* If received on a DLCI hook process for this 33752419Sjulian * channel and pass it to the downstream module. 33852419Sjulian * Normally one would add a multiplexing header at 33952419Sjulian * the front here */ 34052419Sjulian /* M_PREPEND(....) ; */ 34152419Sjulian /* mtod(m, xxxxxx)->dlci = dlci; */ 34269922Sjulian NG_SEND_DATA(error, xxxp->downstream_hook.hook, 34352419Sjulian m, meta); 34452419Sjulian xxxp->packets_out++; 34552419Sjulian } else { 34652419Sjulian /* data came from the multiplexed link */ 34752419Sjulian dlci = 1; /* get dlci from header */ 34852419Sjulian /* madjust(....) *//* chop off header */ 34952419Sjulian for (chan = 0; chan < XXX_NUM_DLCIS; chan++) 35052419Sjulian if (xxxp->channel[chan].dlci == dlci) 35152419Sjulian break; 35252419Sjulian if (chan == XXX_NUM_DLCIS) { 35352419Sjulian NG_FREE_DATA(m, meta); 35452419Sjulian return (ENETUNREACH); 35552419Sjulian } 35652419Sjulian /* If we were called at splnet, use the following: 35752419Sjulian * NG_SEND_DATA(error, otherhook, m, meta); if this 35852419Sjulian * node is running at some SPL other than SPLNET 35952419Sjulian * then you should use instead: error = 36052419Sjulian * ng_queueit(otherhook, m, meta); m = NULL: meta = 36152419Sjulian * NULL; this queues the data using the standard 36252419Sjulian * NETISR system and schedules the data to be picked 36352419Sjulian * up again once the system has moved to SPLNET and 36452419Sjulian * the processing of the data can continue. after 36552419Sjulian * these are run 'm' and 'meta' should be considered 36652419Sjulian * as invalid and NG_SEND_DATA actually zaps them. */ 36752419Sjulian NG_SEND_DATA(error, xxxp->channel[chan].hook, m, meta); 36852419Sjulian xxxp->packets_in++; 36952419Sjulian } 37052419Sjulian } else { 37152419Sjulian /* It's the debug hook, throw it away.. */ 37252419Sjulian if (hook == xxxp->downstream_hook.hook) 37352419Sjulian NG_FREE_DATA(m, meta); 37452419Sjulian } 37552419Sjulian return 0; 37652419Sjulian} 37752419Sjulian 37852419Sjulian#if 0 37952419Sjulian/* 38052419Sjulian * If this were a device node, the data may have been received in response 38152419Sjulian * to some interrupt. 38252419Sjulian * in which case it would probably look as follows: 38352419Sjulian */ 38452419Sjuliandevintr() 38552419Sjulian{ 38669922Sjulian int error; 38752419Sjulian * here */ 38852419Sjulian 38952419Sjulian /* get packet from device and send on */ 39052419Sjulian m = MGET(blah blah) 39169922Sjulian 39269922Sjulian NG_SEND_DATA_ONLY(error, xxxp->upstream_hook.hook, m); 39369922Sjulian /* see note above in xxx_rcvdata() */ 39469922Sjulian /* and ng_xxx_connect() */ 39552419Sjulian} 39652419Sjulian 39752419Sjulian#endif /* 0 */ 39852419Sjulian 39952419Sjulian/* 40052419Sjulian * Do local shutdown processing.. 40152419Sjulian * If we are a persistant device, we might refuse to go away, and 40252419Sjulian * we'd only remove our links and reset ourself. 40352419Sjulian */ 40452419Sjulianstatic int 40552419Sjulianng_xxx_rmnode(node_p node) 40652419Sjulian{ 40752419Sjulian const xxx_p privdata = node->private; 40852419Sjulian 40952419Sjulian node->flags |= NG_INVALID; 41052419Sjulian ng_cutlinks(node); 41152419Sjulian#ifndef PERSISTANT_NODE 41252419Sjulian ng_unname(node); 41352419Sjulian node->private = NULL; 41452419Sjulian ng_unref(privdata->node); 41552419Sjulian FREE(privdata, M_NETGRAPH); 41652419Sjulian#else 41752419Sjulian privdata->packets_in = 0; /* reset stats */ 41852419Sjulian privdata->packets_out = 0; 41952419Sjulian node->flags &= ~NG_INVALID; /* reset invalid flag */ 42052419Sjulian#endif /* PERSISTANT_NODE */ 42152419Sjulian return (0); 42252419Sjulian} 42352419Sjulian 42452419Sjulian/* 42552419Sjulian * This is called once we've already connected a new hook to the other node. 42652419Sjulian * It gives us a chance to balk at the last minute. 42752419Sjulian */ 42852419Sjulianstatic int 42952419Sjulianng_xxx_connect(hook_p hook) 43052419Sjulian{ 43169922Sjulian#if 0 43269922Sjulian /* 43369922Sjulian * If we were a driver running at other than splnet then 43469922Sjulian * we should set the QUEUE bit on the edge so that we 43569922Sjulian * will deliver by queing. 43669922Sjulian */ 43769922Sjulian if /*it is the upstream hook */ 43869922Sjulian hook->peer->flags |= HK_QUEUE; 43969922Sjulian#endif 44069922Sjulian#if 0 44169922Sjulian /* 44269922Sjulian * If for some reason we want incoming date to be queued 44369922Sjulian * by the NETISR system and delivered later we can set the same bit on 44469922Sjulian * OUR hook. (maybe to allow unwinding of the stack) 44569922Sjulian */ 44669922Sjulian 44769922Sjulian if (hook->private) { 44869922Sjulian int dlci; 44969922Sjulian /* 45069922Sjulian * If it's dlci 1023, requeue it so that it's handled 45169922Sjulian * at a lower priority. This is how a node decides to 45269922Sjulian * defer a data message. 45369922Sjulian */ 45469922Sjulian dlci = ((struct XXX_hookinfo *) hook->private)->dlci; 45569922Sjulian if (dlci == 1023) { 45669922Sjulian hook->flags |= HK_QUEUE; 45769922Sjulian } 45869922Sjulian#endif 45969922Sjulian /* otherwise be really amiable and just say "YUP that's OK by me! " */ 46052419Sjulian return (0); 46152419Sjulian} 46252419Sjulian 46352419Sjulian/* 46452419Sjulian * Dook disconnection 46552419Sjulian * 46652419Sjulian * For this type, removal of the last link destroys the node 46752419Sjulian */ 46852419Sjulianstatic int 46952419Sjulianng_xxx_disconnect(hook_p hook) 47052419Sjulian{ 47152419Sjulian if (hook->private) 47253997Sarchie ((struct XXX_hookinfo *) (hook->private))->hook = NULL; 47352419Sjulian if (hook->node->numhooks == 0) 47452419Sjulian ng_rmnode(hook->node); 47552419Sjulian return (0); 47652419Sjulian} 47752419Sjulian 478