ng_sample.c revision 53997
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 * 3752419Sjulian * Author: Julian Elischer <julian@whistle.com> 3852419Sjulian * 3952419Sjulian * $FreeBSD: head/sys/netgraph/ng_sample.c 53997 1999-12-01 19:40:37Z archie $ 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_rcvdata_t ng_xxx_rcvdataq; /* note these are both ng_rcvdata_t */ 6952752Sjulianstatic ng_disconnect_t ng_xxx_disconnect; 7052419Sjulian 7153997Sarchie/* Parse type for struct ngxxxstat */ 7253997Sarchiestatic const struct ng_parse_struct_info 7353997Sarchie ng_xxx_stat_type_info = NG_XXX_STATS_TYPE_INFO; 7453997Sarchiestatic const struct ng_parse_type ng_xxx_stat_type = { 7553997Sarchie &ng_parse_struct_type, 7653997Sarchie &ng_xxx_stat_type_info 7753997Sarchie}; 7853997Sarchie 7953997Sarchie/* List of commands and how to convert arguments to/from ASCII */ 8053997Sarchiestatic const struct ng_cmdlist ng_xxx_cmdlist[] = { 8153997Sarchie { 8253997Sarchie NGM_XXX_COOKIE, 8353997Sarchie NGM_XXX_GET_STATUS, 8453997Sarchie "getstatus", 8553997Sarchie NULL, 8653997Sarchie &ng_xxx_stat_type, 8753997Sarchie }, 8853997Sarchie { 8953997Sarchie NGM_XXX_COOKIE, 9053997Sarchie NGM_XXX_SET_FLAG, 9153997Sarchie "setflag", 9253997Sarchie &ng_parse_int32_type, 9353997Sarchie NULL 9453997Sarchie }, 9553997Sarchie { 0 } 9653997Sarchie}; 9753997Sarchie 9852419Sjulian/* Netgraph node type descriptor */ 9952419Sjulianstatic struct ng_type typestruct = { 10052419Sjulian NG_VERSION, 10152419Sjulian NG_XXX_NODE_TYPE, 10252419Sjulian NULL, 10352419Sjulian ng_xxx_constructor, 10452419Sjulian ng_xxx_rcvmsg, 10552419Sjulian ng_xxx_rmnode, 10652419Sjulian ng_xxx_newhook, 10752419Sjulian NULL, 10852419Sjulian ng_xxx_connect, 10952419Sjulian ng_xxx_rcvdata, 11052419Sjulian ng_xxx_rcvdataq, 11153913Sarchie ng_xxx_disconnect, 11253997Sarchie ng_xxx_cmdlist 11352419Sjulian}; 11452419SjulianNETGRAPH_INIT(xxx, &typestruct); 11552419Sjulian 11652419Sjulian/* Information we store for each hook on each node */ 11752419Sjulianstruct XXX_hookinfo { 11852419Sjulian int dlci; /* The DLCI it represents, -1 == downstream */ 11952419Sjulian int channel; /* The channel representing this DLCI */ 12052419Sjulian hook_p hook; 12152419Sjulian}; 12252419Sjulian 12352419Sjulian/* Information we store for each node */ 12452419Sjulianstruct XXX { 12552419Sjulian struct XXX_hookinfo channel[XXX_NUM_DLCIS]; 12652419Sjulian struct XXX_hookinfo downstream_hook; 12752419Sjulian node_p node; /* back pointer to node */ 12852419Sjulian hook_p debughook; 12952419Sjulian u_int packets_in; /* packets in from downstream */ 13052419Sjulian u_int packets_out; /* packets out towards downstream */ 13152419Sjulian u_int32_t flags; 13252419Sjulian}; 13352419Sjuliantypedef struct XXX *xxx_p; 13452419Sjulian 13552419Sjulian/* 13652419Sjulian * Allocate the private data structure and the generic node 13752419Sjulian * and link them together. 13852419Sjulian * 13952419Sjulian * ng_make_node_common() returns with a generic node struct 14052419Sjulian * with a single reference for us.. we transfer it to the 14152419Sjulian * private structure.. when we free the private struct we must 14252419Sjulian * unref the node so it gets freed too. 14352419Sjulian * 14452419Sjulian * If this were a device node than this work would be done in the attach() 14552419Sjulian * routine and the constructor would return EINVAL as you should not be able 14652419Sjulian * to creatednodes that depend on hardware (unless you can add the hardware :) 14752419Sjulian */ 14852419Sjulianstatic int 14952419Sjulianng_xxx_constructor(node_p *nodep) 15052419Sjulian{ 15152419Sjulian xxx_p privdata; 15252419Sjulian int i, error; 15352419Sjulian 15452419Sjulian /* Initialize private descriptor */ 15552419Sjulian MALLOC(privdata, xxx_p, sizeof(*privdata), M_NETGRAPH, M_WAITOK); 15652419Sjulian if (privdata == NULL) 15752419Sjulian return (ENOMEM); 15852419Sjulian bzero(privdata, sizeof(struct XXX)); 15952419Sjulian for (i = 0; i < XXX_NUM_DLCIS; i++) { 16052419Sjulian privdata->channel[i].dlci = -2; 16152419Sjulian privdata->channel[i].channel = i; 16252419Sjulian } 16352419Sjulian 16452419Sjulian /* Call the 'generic' (ie, superclass) node constructor */ 16552419Sjulian if ((error = ng_make_node_common(&typestruct, nodep))) { 16652419Sjulian FREE(privdata, M_NETGRAPH); 16752419Sjulian return (error); 16852419Sjulian } 16952419Sjulian 17052419Sjulian /* Link structs together; this counts as our one reference to *nodep */ 17152419Sjulian (*nodep)->private = privdata; 17252419Sjulian privdata->node = *nodep; 17352419Sjulian return (0); 17452419Sjulian} 17552419Sjulian 17652419Sjulian/* 17752419Sjulian * Give our ok for a hook to be added... 17852419Sjulian * If we are not running this might kick a device into life. 17952419Sjulian * Possibly decode information out of the hook name. 18052419Sjulian * Add the hook's private info to the hook structure. 18152419Sjulian * (if we had some). In this example, we assume that there is a 18252419Sjulian * an array of structs, called 'channel' in the private info, 18352419Sjulian * one for each active channel. The private 18452419Sjulian * pointer of each hook points to the appropriate XXX_hookinfo struct 18552419Sjulian * so that the source of an input packet is easily identified. 18652419Sjulian * (a dlci is a frame relay channel) 18752419Sjulian */ 18852419Sjulianstatic int 18952419Sjulianng_xxx_newhook(node_p node, hook_p hook, const char *name) 19052419Sjulian{ 19152419Sjulian const xxx_p xxxp = node->private; 19252419Sjulian const char *cp; 19352419Sjulian int dlci = 0; 19452419Sjulian int chan; 19552419Sjulian 19652419Sjulian#if 0 19752419Sjulian /* Possibly start up the device if it's not already going */ 19852419Sjulian if ((xxxp->flags & SCF_RUNNING) == 0) { 19952419Sjulian ng_xxx_start_hardware(xxxp); 20052419Sjulian } 20152419Sjulian#endif 20252419Sjulian 20352419Sjulian /* Example of how one might use hooks with embedded numbers: All 20452419Sjulian * hooks start with 'dlci' and have a decimal trailing channel 20552419Sjulian * number up to 4 digits Use the leadin defined int he associated .h 20652419Sjulian * file. */ 20752816Sarchie if (strncmp(name, 20852816Sarchie NG_XXX_HOOK_DLCI_LEADIN, strlen(NG_XXX_HOOK_DLCI_LEADIN)) == 0) { 20953648Sarchie char *eptr; 21052816Sarchie 21152419Sjulian cp = name + sizeof(NG_XXX_HOOK_DLCI_LEADIN); 21252816Sarchie if (!isdigit(*cp) || (cp[0] == '0' && cp[1] != '\0')) 21352419Sjulian return (EINVAL); 21452816Sarchie dlci = (int)strtoul(cp, &eptr, 10); 21552816Sarchie if (*eptr != '\0' || dlci < 0 || dlci > 1023) 21652816Sarchie return (EINVAL); 21752816Sarchie 21852419Sjulian /* We have a dlci, now either find it, or allocate it */ 21952419Sjulian for (chan = 0; chan < XXX_NUM_DLCIS; chan++) 22052419Sjulian if (xxxp->channel[chan].dlci == dlci) 22152419Sjulian break; 22252419Sjulian if (chan == XXX_NUM_DLCIS) { 22352419Sjulian for (chan = 0; chan < XXX_NUM_DLCIS; chan++) 22452419Sjulian if (xxxp->channel[chan].dlci != -2) 22552419Sjulian continue; 22652419Sjulian if (chan == XXX_NUM_DLCIS) 22752419Sjulian return (ENOBUFS); 22852419Sjulian } 22952419Sjulian if (xxxp->channel[chan].hook != NULL) 23052419Sjulian return (EADDRINUSE); 23152419Sjulian hook->private = xxxp->channel + chan; 23252419Sjulian xxxp->channel[chan].hook = hook; 23352419Sjulian return (0); 23452419Sjulian } else if (strcmp(name, NG_XXX_HOOK_DOWNSTREAM) == 0) { 23552419Sjulian /* Example of simple predefined hooks. */ 23652419Sjulian /* do something specific to the downstream connection */ 23752419Sjulian xxxp->downstream_hook.hook = hook; 23852419Sjulian hook->private = &xxxp->downstream_hook; 23952419Sjulian } else if (strcmp(name, NG_XXX_HOOK_DEBUG) == 0) { 24052419Sjulian /* do something specific to a debug connection */ 24152419Sjulian xxxp->debughook = hook; 24252419Sjulian hook->private = NULL; 24352419Sjulian } else 24452419Sjulian return (EINVAL); /* not a hook we know about */ 24552419Sjulian return(0); 24652419Sjulian} 24752419Sjulian 24852419Sjulian/* 24952419Sjulian * Get a netgraph control message. 25052419Sjulian * Check it is one we understand. If needed, send a response. 25152419Sjulian * We could save the address for an async action later, but don't here. 25252419Sjulian * Always free the message. 25352419Sjulian * The response should be in a malloc'd region that the caller can 'free'. 25452419Sjulian * A response is not required. 25552419Sjulian * Theoretically you could respond defferently to old message types if 25652419Sjulian * the cookie in the header didn't match what we consider to be current 25752419Sjulian * (so that old userland programs could continue to work). 25852419Sjulian */ 25952419Sjulianstatic int 26052419Sjulianng_xxx_rcvmsg(node_p node, 26152419Sjulian struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr) 26252419Sjulian{ 26352419Sjulian const xxx_p xxxp = node->private; 26452419Sjulian struct ng_mesg *resp = NULL; 26552419Sjulian int error = 0; 26652419Sjulian 26752419Sjulian /* Deal with message according to cookie and command */ 26852419Sjulian switch (msg->header.typecookie) { 26952419Sjulian case NGM_XXX_COOKIE: 27052419Sjulian switch (msg->header.cmd) { 27152419Sjulian case NGM_XXX_GET_STATUS: 27252419Sjulian { 27352419Sjulian struct ngxxxstat *stats; 27452419Sjulian 27552419Sjulian NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 27652419Sjulian if (!resp) { 27752419Sjulian error = ENOMEM; 27852419Sjulian break; 27952419Sjulian } 28052419Sjulian stats = (struct ngxxxstat *) resp->data; 28152419Sjulian stats->packets_in = xxxp->packets_in; 28252419Sjulian stats->packets_out = xxxp->packets_out; 28352419Sjulian break; 28452419Sjulian } 28552419Sjulian case NGM_XXX_SET_FLAG: 28652419Sjulian if (msg->header.arglen != sizeof(u_int32_t)) { 28752419Sjulian error = EINVAL; 28852419Sjulian break; 28952419Sjulian } 29052419Sjulian xxxp->flags = *((u_int32_t *) msg->data); 29152419Sjulian break; 29252419Sjulian default: 29352419Sjulian error = EINVAL; /* unknown command */ 29452419Sjulian break; 29552419Sjulian } 29652419Sjulian break; 29752419Sjulian default: 29852419Sjulian error = EINVAL; /* unknown cookie type */ 29952419Sjulian break; 30052419Sjulian } 30152419Sjulian 30252419Sjulian /* Take care of synchronous response, if any */ 30352419Sjulian if (rptr) 30452419Sjulian *rptr = resp; 30552419Sjulian else if (resp) 30652419Sjulian FREE(resp, M_NETGRAPH); 30752419Sjulian 30852419Sjulian /* Free the message and return */ 30952419Sjulian FREE(msg, M_NETGRAPH); 31052419Sjulian return(error); 31152419Sjulian} 31252419Sjulian 31352419Sjulian/* 31452419Sjulian * Receive data, and do something with it. 31552419Sjulian * Possibly send it out on another link after processing. 31652419Sjulian * Possibly do something different if it comes from different 31752419Sjulian * hooks. the caller will never free m or meta, so 31852419Sjulian * if we use up this data or abort we must free BOTH of these. 31952419Sjulian * 32052419Sjulian * If we want, we may decide to force this data to be queued and reprocessed 32152419Sjulian * at the netgraph NETISR time. (at which time it will be entered using ng_xxx_rcvdataq(). 32252419Sjulian */ 32352419Sjulianstatic int 32452419Sjulianng_xxx_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) 32552419Sjulian{ 32652419Sjulian int dlci = -2; 32752419Sjulian 32852419Sjulian if (hook->private) { 32952419Sjulian /* 33053997Sarchie * If it's dlci 1023, requeue it so that it's handled 33153997Sarchie * at a lower priority. This is how a node decides to 33253997Sarchie * defer a data message. 33352419Sjulian */ 33452419Sjulian dlci = ((struct XXX_hookinfo *) hook->private)->dlci; 33552419Sjulian if (dlci == 1023) { 33652736Sjulian return(ng_queue_data(hook->peer, m, meta)); 33752419Sjulian } 33852419Sjulian } 33952736Sjulian return(ng_xxx_rcvdataq(hook, m, meta)); 34052419Sjulian} 34152419Sjulian 34252419Sjulian/* 34352419Sjulian * Always accept the data. This version of rcvdata is called from the dequeueing routine. 34452419Sjulian */ 34552419Sjulianstatic int 34652419Sjulianng_xxx_rcvdataq(hook_p hook, struct mbuf *m, meta_p meta) 34752419Sjulian{ 34852419Sjulian const xxx_p xxxp = hook->node->private; 34952419Sjulian int chan = -2; 35052419Sjulian int dlci = -2; 35152419Sjulian int error; 35252419Sjulian 35352419Sjulian if (hook->private) { 35452419Sjulian dlci = ((struct XXX_hookinfo *) hook->private)->dlci; 35552419Sjulian chan = ((struct XXX_hookinfo *) hook->private)->channel; 35652419Sjulian if (dlci != -1) { 35752419Sjulian /* If received on a DLCI hook process for this 35852419Sjulian * channel and pass it to the downstream module. 35952419Sjulian * Normally one would add a multiplexing header at 36052419Sjulian * the front here */ 36152419Sjulian /* M_PREPEND(....) ; */ 36252419Sjulian /* mtod(m, xxxxxx)->dlci = dlci; */ 36352419Sjulian error = ng_send_data(xxxp->downstream_hook.hook, 36452419Sjulian m, meta); 36552419Sjulian xxxp->packets_out++; 36652419Sjulian } else { 36752419Sjulian /* data came from the multiplexed link */ 36852419Sjulian dlci = 1; /* get dlci from header */ 36952419Sjulian /* madjust(....) *//* chop off header */ 37052419Sjulian for (chan = 0; chan < XXX_NUM_DLCIS; chan++) 37152419Sjulian if (xxxp->channel[chan].dlci == dlci) 37252419Sjulian break; 37352419Sjulian if (chan == XXX_NUM_DLCIS) { 37452419Sjulian NG_FREE_DATA(m, meta); 37552419Sjulian return (ENETUNREACH); 37652419Sjulian } 37752419Sjulian /* If we were called at splnet, use the following: 37852419Sjulian * NG_SEND_DATA(error, otherhook, m, meta); if this 37952419Sjulian * node is running at some SPL other than SPLNET 38052419Sjulian * then you should use instead: error = 38152419Sjulian * ng_queueit(otherhook, m, meta); m = NULL: meta = 38252419Sjulian * NULL; this queues the data using the standard 38352419Sjulian * NETISR system and schedules the data to be picked 38452419Sjulian * up again once the system has moved to SPLNET and 38552419Sjulian * the processing of the data can continue. after 38652419Sjulian * these are run 'm' and 'meta' should be considered 38752419Sjulian * as invalid and NG_SEND_DATA actually zaps them. */ 38852419Sjulian NG_SEND_DATA(error, xxxp->channel[chan].hook, m, meta); 38952419Sjulian xxxp->packets_in++; 39052419Sjulian } 39152419Sjulian } else { 39252419Sjulian /* It's the debug hook, throw it away.. */ 39352419Sjulian if (hook == xxxp->downstream_hook.hook) 39452419Sjulian NG_FREE_DATA(m, meta); 39552419Sjulian } 39652419Sjulian return 0; 39752419Sjulian} 39852419Sjulian 39952419Sjulian#if 0 40052419Sjulian/* 40152419Sjulian * If this were a device node, the data may have been received in response 40252419Sjulian * to some interrupt. 40352419Sjulian * in which case it would probably look as follows: 40452419Sjulian */ 40552419Sjuliandevintr() 40652419Sjulian{ 40752419Sjulian meta_p meta = NULL; /* whatever metadata we might imagine goes 40852419Sjulian * here */ 40952419Sjulian 41052419Sjulian /* get packet from device and send on */ 41152419Sjulian m = MGET(blah blah) 41252419Sjulian error = ng_queueit(upstream, m, meta); /* see note above in 41352419Sjulian * xxx_rcvdata() */ 41452419Sjulian} 41552419Sjulian 41652419Sjulian#endif /* 0 */ 41752419Sjulian 41852419Sjulian/* 41952419Sjulian * Do local shutdown processing.. 42052419Sjulian * If we are a persistant device, we might refuse to go away, and 42152419Sjulian * we'd only remove our links and reset ourself. 42252419Sjulian */ 42352419Sjulianstatic int 42452419Sjulianng_xxx_rmnode(node_p node) 42552419Sjulian{ 42652419Sjulian const xxx_p privdata = node->private; 42752419Sjulian 42852419Sjulian node->flags |= NG_INVALID; 42952419Sjulian ng_cutlinks(node); 43052419Sjulian#ifndef PERSISTANT_NODE 43152419Sjulian ng_unname(node); 43252419Sjulian node->private = NULL; 43352419Sjulian ng_unref(privdata->node); 43452419Sjulian FREE(privdata, M_NETGRAPH); 43552419Sjulian#else 43652419Sjulian privdata->packets_in = 0; /* reset stats */ 43752419Sjulian privdata->packets_out = 0; 43852419Sjulian node->flags &= ~NG_INVALID; /* reset invalid flag */ 43952419Sjulian#endif /* PERSISTANT_NODE */ 44052419Sjulian return (0); 44152419Sjulian} 44252419Sjulian 44352419Sjulian/* 44452419Sjulian * This is called once we've already connected a new hook to the other node. 44552419Sjulian * It gives us a chance to balk at the last minute. 44652419Sjulian */ 44752419Sjulianstatic int 44852419Sjulianng_xxx_connect(hook_p hook) 44952419Sjulian{ 45052419Sjulian /* be really amiable and just say "YUP that's OK by me! " */ 45152419Sjulian return (0); 45252419Sjulian} 45352419Sjulian 45452419Sjulian/* 45552419Sjulian * Dook disconnection 45652419Sjulian * 45752419Sjulian * For this type, removal of the last link destroys the node 45852419Sjulian */ 45952419Sjulianstatic int 46052419Sjulianng_xxx_disconnect(hook_p hook) 46152419Sjulian{ 46252419Sjulian if (hook->private) 46353997Sarchie ((struct XXX_hookinfo *) (hook->private))->hook = NULL; 46452419Sjulian if (hook->node->numhooks == 0) 46552419Sjulian ng_rmnode(hook->node); 46652419Sjulian return (0); 46752419Sjulian} 46852419Sjulian 469