185722Sjulian/*-
285722Sjulian * ng_etf.c  Ethertype filter
3139823Simp */
4139823Simp
5139823Simp/*-
685722Sjulian * Copyright (c) 2001, FreeBSD Incorporated
785722Sjulian * All rights reserved.
885722Sjulian *
985722Sjulian * Redistribution and use in source and binary forms, with or without
1085722Sjulian * modification, are permitted provided that the following conditions
1185722Sjulian * are met:
1285722Sjulian * 1. Redistributions of source code must retain the above copyright
1385722Sjulian *    notice unmodified, this list of conditions, and the following
1485722Sjulian *    disclaimer.
1585722Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1685722Sjulian *    notice, this list of conditions and the following disclaimer in the
1785722Sjulian *    documentation and/or other materials provided with the distribution.
1885722Sjulian *
1985722Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2085722Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2185722Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2285722Sjulian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2385722Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2485722Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2585722Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2685722Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2785722Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2885722Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2985722Sjulian * SUCH DAMAGE.
3085722Sjulian *
3185722Sjulian * Author: Julian Elischer <julian@freebsd.org>
3285722Sjulian *
3385722Sjulian * $FreeBSD$
3485722Sjulian */
3585722Sjulian
3685722Sjulian#include <sys/param.h>
3785722Sjulian#include <sys/systm.h>
3885722Sjulian#include <sys/kernel.h>
3985722Sjulian#include <sys/mbuf.h>
4085722Sjulian#include <sys/malloc.h>
4185722Sjulian#include <sys/ctype.h>
4285722Sjulian#include <sys/errno.h>
4385722Sjulian#include <sys/queue.h>
4485722Sjulian#include <sys/syslog.h>
4585722Sjulian
4685722Sjulian#include <net/ethernet.h>
4785722Sjulian
4885722Sjulian#include <netgraph/ng_message.h>
4985722Sjulian#include <netgraph/ng_parse.h>
5085722Sjulian#include <netgraph/ng_etf.h>
5185722Sjulian#include <netgraph/netgraph.h>
5285722Sjulian
5385722Sjulian/* If you do complicated mallocs you may want to do this */
5485722Sjulian/* and use it for your mallocs */
5585722Sjulian#ifdef NG_SEPARATE_MALLOC
56141635Sphkstatic MALLOC_DEFINE(M_NETGRAPH_ETF, "netgraph_etf", "netgraph etf node ");
5785722Sjulian#else
5885722Sjulian#define M_NETGRAPH_ETF M_NETGRAPH
5985722Sjulian#endif
6085722Sjulian
6185722Sjulian/*
6285722Sjulian * This section contains the netgraph method declarations for the
6385722Sjulian * etf node. These methods define the netgraph 'type'.
6485722Sjulian */
6585722Sjulian
6685722Sjulianstatic ng_constructor_t	ng_etf_constructor;
6785722Sjulianstatic ng_rcvmsg_t	ng_etf_rcvmsg;
6885722Sjulianstatic ng_shutdown_t	ng_etf_shutdown;
6985722Sjulianstatic ng_newhook_t	ng_etf_newhook;
7085722Sjulianstatic ng_rcvdata_t	ng_etf_rcvdata;	 /* note these are both ng_rcvdata_t */
7185722Sjulianstatic ng_disconnect_t	ng_etf_disconnect;
7285722Sjulian
7385722Sjulian/* Parse type for struct ng_etfstat */
7497685Sarchiestatic const struct ng_parse_struct_field ng_etf_stat_type_fields[]
7597685Sarchie	= NG_ETF_STATS_TYPE_INFO;
7685722Sjulianstatic const struct ng_parse_type ng_etf_stat_type = {
7785722Sjulian	&ng_parse_struct_type,
7897685Sarchie	&ng_etf_stat_type_fields
7985722Sjulian};
8085722Sjulian/* Parse type for struct ng_setfilter */
8197685Sarchiestatic const struct ng_parse_struct_field ng_etf_filter_type_fields[]
8297685Sarchie	= NG_ETF_FILTER_TYPE_INFO;
8385722Sjulianstatic const struct ng_parse_type ng_etf_filter_type = {
8485722Sjulian	&ng_parse_struct_type,
8597685Sarchie	&ng_etf_filter_type_fields
8685722Sjulian};
8785722Sjulian
8885722Sjulian/* List of commands and how to convert arguments to/from ASCII */
8985722Sjulianstatic const struct ng_cmdlist ng_etf_cmdlist[] = {
9085722Sjulian	{
9185722Sjulian	  NGM_ETF_COOKIE,
9285722Sjulian	  NGM_ETF_GET_STATUS,
9385722Sjulian	  "getstatus",
9485722Sjulian	  NULL,
9585722Sjulian	  &ng_etf_stat_type,
9685722Sjulian	},
9785722Sjulian	{
9885722Sjulian	  NGM_ETF_COOKIE,
9985722Sjulian	  NGM_ETF_SET_FLAG,
10085722Sjulian	  "setflag",
10185722Sjulian	  &ng_parse_int32_type,
10285722Sjulian	  NULL
10385722Sjulian	},
10485722Sjulian	{
10585722Sjulian	  NGM_ETF_COOKIE,
10685722Sjulian	  NGM_ETF_SET_FILTER,
10785722Sjulian	  "setfilter",
10885722Sjulian	  &ng_etf_filter_type,
10985722Sjulian	  NULL
11085722Sjulian	},
11185722Sjulian	{ 0 }
11285722Sjulian};
11385722Sjulian
11485722Sjulian/* Netgraph node type descriptor */
11585722Sjulianstatic struct ng_type typestruct = {
116129823Sjulian	.version =	NG_ABI_VERSION,
117129823Sjulian	.name =		NG_ETF_NODE_TYPE,
118129823Sjulian	.constructor =	ng_etf_constructor,
119129823Sjulian	.rcvmsg =	ng_etf_rcvmsg,
120129823Sjulian	.shutdown =	ng_etf_shutdown,
121129823Sjulian	.newhook =	ng_etf_newhook,
122129823Sjulian	.rcvdata =	ng_etf_rcvdata,
123129823Sjulian	.disconnect =	ng_etf_disconnect,
124129823Sjulian	.cmdlist =	ng_etf_cmdlist,
12585722Sjulian};
12685722SjulianNETGRAPH_INIT(etf, &typestruct);
12785722Sjulian
12885722Sjulian/* Information we store for each hook on each node */
12985722Sjulianstruct ETF_hookinfo {
13085722Sjulian	hook_p  hook;
13185722Sjulian};
13285722Sjulian
13385722Sjulianstruct filter {
13485722Sjulian	LIST_ENTRY(filter) next;
13585722Sjulian	u_int16_t	ethertype;	/* network order ethertype */
13685722Sjulian	hook_p		match_hook;	/* Hook to use on a match */
13785722Sjulian};
13885722Sjulian
13985722Sjulian#define HASHSIZE 16 /* Dont change this without changing HASH() */
14085722Sjulian#define HASH(et) ((((et)>>12)+((et)>>8)+((et)>>4)+(et)) & 0x0f)
14185722SjulianLIST_HEAD(filterhead, filter);
14285722Sjulian
14385722Sjulian/* Information we store for each node */
14485722Sjulianstruct ETF {
14585722Sjulian	struct ETF_hookinfo downstream_hook;
14685722Sjulian	struct ETF_hookinfo nomatch_hook;
14785722Sjulian	node_p		node;		/* back pointer to node */
14885722Sjulian	u_int   	packets_in;	/* packets in from downstream */
14985722Sjulian	u_int   	packets_out;	/* packets out towards downstream */
15085722Sjulian	u_int32_t	flags;
15185722Sjulian	struct filterhead hashtable[HASHSIZE];
15285722Sjulian};
15385722Sjuliantypedef struct ETF *etf_p;
15485722Sjulian
15585722Sjulianstatic struct filter *
15685722Sjulianng_etf_findentry(etf_p etfp, u_int16_t ethertype)
15785722Sjulian{
15885722Sjulian	struct filterhead *chain = etfp->hashtable + HASH(ethertype);
15985722Sjulian	struct filter *fil;
16085722Sjulian
16185722Sjulian
16285722Sjulian	LIST_FOREACH(fil, chain, next) {
16385722Sjulian		if (fil->ethertype == ethertype) {
16485722Sjulian			return (fil);
16585722Sjulian		}
16685722Sjulian	}
16785722Sjulian	return (NULL);
16885722Sjulian}
16985722Sjulian
17085722Sjulian
17185722Sjulian/*
17285722Sjulian * Allocate the private data structure. The generic node has already
17385722Sjulian * been created. Link them together. We arrive with a reference to the node
17485722Sjulian * i.e. the reference count is incremented for us already.
17585722Sjulian */
17685722Sjulianstatic int
17785722Sjulianng_etf_constructor(node_p node)
17885722Sjulian{
17985722Sjulian	etf_p privdata;
18085722Sjulian	int i;
18185722Sjulian
18285722Sjulian	/* Initialize private descriptor */
183220768Sglebius	privdata = malloc(sizeof(*privdata), M_NETGRAPH_ETF, M_WAITOK | M_ZERO);
18485722Sjulian	for (i = 0; i < HASHSIZE; i++) {
18585722Sjulian		LIST_INIT((privdata->hashtable + i));
18685722Sjulian	}
18785722Sjulian
18885722Sjulian	/* Link structs together; this counts as our one reference to node */
18985722Sjulian	NG_NODE_SET_PRIVATE(node, privdata);
19085722Sjulian	privdata->node = node;
19185722Sjulian	return (0);
19285722Sjulian}
19385722Sjulian
19485722Sjulian/*
19585722Sjulian * Give our ok for a hook to be added...
19685722Sjulian * All names are ok. Two names are special.
19785722Sjulian */
19885722Sjulianstatic int
19985722Sjulianng_etf_newhook(node_p node, hook_p hook, const char *name)
20085722Sjulian{
20185722Sjulian	const etf_p etfp = NG_NODE_PRIVATE(node);
20285722Sjulian	struct ETF_hookinfo *hpriv;
20385722Sjulian
20485722Sjulian	if (strcmp(name, NG_ETF_HOOK_DOWNSTREAM) == 0) {
20585722Sjulian		etfp->downstream_hook.hook = hook;
20685722Sjulian		NG_HOOK_SET_PRIVATE(hook, &etfp->downstream_hook);
20785722Sjulian		etfp->packets_in = 0;
20885722Sjulian		etfp->packets_out = 0;
20985722Sjulian	} else if (strcmp(name, NG_ETF_HOOK_NOMATCH) == 0) {
21085722Sjulian		etfp->nomatch_hook.hook = hook;
21185722Sjulian		NG_HOOK_SET_PRIVATE(hook, &etfp->nomatch_hook);
21285722Sjulian	} else {
21385722Sjulian		/*
21485722Sjulian		 * Any other hook name is valid and can
21585722Sjulian		 * later be associated with a filter rule.
21685722Sjulian		 */
217184205Sdes		hpriv = malloc(sizeof(*hpriv),
21885722Sjulian			M_NETGRAPH_ETF, M_NOWAIT | M_ZERO);
21985722Sjulian		if (hpriv == NULL) {
22085722Sjulian			return (ENOMEM);
22185722Sjulian		}
22285722Sjulian
22385722Sjulian		NG_HOOK_SET_PRIVATE(hook, hpriv);
22485722Sjulian		hpriv->hook = hook;
22585722Sjulian	}
22685722Sjulian	return(0);
22785722Sjulian}
22885722Sjulian
22985722Sjulian/*
23085722Sjulian * Get a netgraph control message.
23185722Sjulian * We actually recieve a queue item that has a pointer to the message.
23285722Sjulian * If we free the item, the message will be freed too, unless we remove
23385722Sjulian * it from the item using NGI_GET_MSG();
23485722Sjulian * The return address is also stored in the item, as an ng_ID_t,
23585722Sjulian * accessible as NGI_RETADDR(item);
23685722Sjulian * Check it is one we understand. If needed, send a response.
23785722Sjulian * We could save the address for an async action later, but don't here.
23885722Sjulian * Always free the message.
23985722Sjulian * The response should be in a malloc'd region that the caller can 'free'.
24085722Sjulian * The NG_MKRESPONSE macro does all this for us.
24185722Sjulian * A response is not required.
24285722Sjulian * Theoretically you could respond defferently to old message types if
24385722Sjulian * the cookie in the header didn't match what we consider to be current
24485722Sjulian * (so that old userland programs could continue to work).
24585722Sjulian */
24685722Sjulianstatic int
24785722Sjulianng_etf_rcvmsg(node_p node, item_p item, hook_p lasthook)
24885722Sjulian{
24985722Sjulian	const etf_p etfp = NG_NODE_PRIVATE(node);
25085722Sjulian	struct ng_mesg *resp = NULL;
25185722Sjulian	int error = 0;
25285722Sjulian	struct ng_mesg *msg;
25385722Sjulian
25485722Sjulian	NGI_GET_MSG(item, msg);
25585722Sjulian	/* Deal with message according to cookie and command */
25685722Sjulian	switch (msg->header.typecookie) {
25785722Sjulian	case NGM_ETF_COOKIE:
25885722Sjulian		switch (msg->header.cmd) {
25985722Sjulian		case NGM_ETF_GET_STATUS:
26085722Sjulian		    {
26185722Sjulian			struct ng_etfstat *stats;
26285722Sjulian
26385722Sjulian			NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
26485722Sjulian			if (!resp) {
26585722Sjulian				error = ENOMEM;
26685722Sjulian				break;
26785722Sjulian			}
26885722Sjulian			stats = (struct ng_etfstat *) resp->data;
26985722Sjulian			stats->packets_in = etfp->packets_in;
27085722Sjulian			stats->packets_out = etfp->packets_out;
27185722Sjulian			break;
27285722Sjulian		    }
27385722Sjulian		case NGM_ETF_SET_FLAG:
27485722Sjulian			if (msg->header.arglen != sizeof(u_int32_t)) {
27585722Sjulian				error = EINVAL;
27685722Sjulian				break;
27785722Sjulian			}
27885722Sjulian			etfp->flags = *((u_int32_t *) msg->data);
27985722Sjulian			break;
28085722Sjulian		case NGM_ETF_SET_FILTER:
28185722Sjulian			{
28285722Sjulian				struct ng_etffilter *f;
28385722Sjulian				struct filter *fil;
28485722Sjulian				hook_p  hook;
28585722Sjulian
28685722Sjulian				/* Check message long enough for this command */
28785722Sjulian				if (msg->header.arglen != sizeof(*f)) {
28885722Sjulian					error = EINVAL;
28985722Sjulian					break;
29085722Sjulian				}
29185722Sjulian
29285722Sjulian				/* Make sure hook referenced exists */
29385722Sjulian				f = (struct ng_etffilter *)msg->data;
29485722Sjulian				hook = ng_findhook(node, f->matchhook);
29585722Sjulian				if (hook == NULL) {
29685722Sjulian					error = ENOENT;
29785722Sjulian					break;
29885722Sjulian				}
29985722Sjulian
30085722Sjulian				/* and is not the downstream hook */
30185722Sjulian				if (hook == etfp->downstream_hook.hook) {
30285722Sjulian					error = EINVAL;
30385722Sjulian					break;
30485722Sjulian				}
30585722Sjulian
30685722Sjulian				/* Check we don't already trap this ethertype */
30785722Sjulian				if (ng_etf_findentry(etfp,
30885722Sjulian						htons(f->ethertype))) {
30985722Sjulian					error = EEXIST;
31085722Sjulian					break;
31185722Sjulian				}
31285722Sjulian
31385722Sjulian				/*
31485722Sjulian				 * Ok, make the filter and put it in the
31585722Sjulian				 * hashtable ready for matching.
31685722Sjulian				 */
317184205Sdes				fil = malloc(sizeof(*fil),
31885722Sjulian					M_NETGRAPH_ETF, M_NOWAIT | M_ZERO);
31985722Sjulian				if (fil == NULL) {
320122865Sru					error = ENOMEM;
321122865Sru					break;
32285722Sjulian				}
32385722Sjulian
32485722Sjulian				fil->match_hook = hook;
32585722Sjulian				fil->ethertype = htons(f->ethertype);
32685722Sjulian				LIST_INSERT_HEAD( etfp->hashtable
32785722Sjulian					+ HASH(fil->ethertype),
32885722Sjulian						fil, next);
32985722Sjulian			}
33085722Sjulian			break;
33185722Sjulian		default:
33285722Sjulian			error = EINVAL;		/* unknown command */
33385722Sjulian			break;
33485722Sjulian		}
33585722Sjulian		break;
33685722Sjulian	default:
33785722Sjulian		error = EINVAL;			/* unknown cookie type */
33885722Sjulian		break;
33985722Sjulian	}
34085722Sjulian
34185722Sjulian	/* Take care of synchronous response, if any */
34285722Sjulian	NG_RESPOND_MSG(error, node, item, resp);
34385722Sjulian	/* Free the message and return */
34485722Sjulian	NG_FREE_MSG(msg);
34585722Sjulian	return(error);
34685722Sjulian}
34785722Sjulian
34885722Sjulian/*
34985722Sjulian * Receive data, and do something with it.
35085722Sjulian * Actually we receive a queue item which holds the data.
351131155Sjulian * If we free the item it will also free the data unless we have previously
352131155Sjulian * disassociated it using the NGI_GET_etf() macro.
35385722Sjulian * Possibly send it out on another link after processing.
35485722Sjulian * Possibly do something different if it comes from different
355131155Sjulian * hooks. The caller will never free m , so if we use up this data
356131155Sjulian * or abort we must free it.
35785722Sjulian *
35885722Sjulian * If we want, we may decide to force this data to be queued and reprocessed
35985722Sjulian * at the netgraph NETISR time.
36085722Sjulian * We would do that by setting the HK_QUEUE flag on our hook. We would do that
36185722Sjulian * in the connect() method.
36285722Sjulian */
36385722Sjulianstatic int
36485722Sjulianng_etf_rcvdata(hook_p hook, item_p item )
36585722Sjulian{
36685722Sjulian	const etf_p etfp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
36785722Sjulian	struct ether_header *eh;
36885722Sjulian	int error = 0;
36985722Sjulian	struct mbuf *m;
37085722Sjulian	u_int16_t ethertype;
37185722Sjulian	struct filter *fil;
37285722Sjulian
37385722Sjulian	if (NG_HOOK_PRIVATE(hook) == NULL) { /* Shouldn't happen but.. */
37485722Sjulian		NG_FREE_ITEM(item);
37585722Sjulian	}
37685722Sjulian
37785722Sjulian	/*
37885722Sjulian	 * Everything not from the downstream hook goes to the
37985722Sjulian	 * downstream hook. But only if it matches the ethertype
38085722Sjulian	 * of the source hook. Un matching must go to/from 'nomatch'.
38185722Sjulian	 */
38285722Sjulian
38385722Sjulian	/* Make sure we have an entire header */
38485722Sjulian	NGI_GET_M(item, m);
38585722Sjulian	if (m->m_len < sizeof(*eh) ) {
38685722Sjulian		m = m_pullup(m, sizeof(*eh));
38785722Sjulian		if (m == NULL) {
38885722Sjulian			NG_FREE_ITEM(item);
38985722Sjulian			return(EINVAL);
39085722Sjulian		}
39185722Sjulian	}
39285722Sjulian
39385722Sjulian	eh = mtod(m, struct ether_header *);
39485722Sjulian	ethertype = eh->ether_type;
39585722Sjulian	fil = ng_etf_findentry(etfp, ethertype);
39685722Sjulian
39785722Sjulian	/*
39885722Sjulian	 * if from downstream, select between a match hook or
39985722Sjulian	 * the nomatch hook
40085722Sjulian	 */
40185722Sjulian	if (hook == etfp->downstream_hook.hook) {
40285722Sjulian		etfp->packets_in++;
40385722Sjulian		if (fil && fil->match_hook) {
40485722Sjulian			NG_FWD_NEW_DATA(error, item, fil->match_hook, m);
40585722Sjulian		} else {
40685722Sjulian			NG_FWD_NEW_DATA(error, item,etfp->nomatch_hook.hook, m);
40785722Sjulian		}
40885722Sjulian	} else {
40985722Sjulian		/*
41085722Sjulian		 * It must be heading towards the downstream.
41185722Sjulian		 * Check that it's ethertype matches
41285722Sjulian		 * the filters for it's input hook.
41385722Sjulian		 * If it doesn't have one, check it's from nomatch.
41485722Sjulian		 */
41585722Sjulian		if ((fil && (fil->match_hook != hook))
41685722Sjulian		|| ((fil == NULL) && (hook != etfp->nomatch_hook.hook))) {
41785722Sjulian			NG_FREE_ITEM(item);
41885722Sjulian			NG_FREE_M(m);
41985722Sjulian			return (EPROTOTYPE);
42085722Sjulian		}
42185722Sjulian		NG_FWD_NEW_DATA( error, item, etfp->downstream_hook.hook, m);
42285722Sjulian		if (error == 0) {
42385722Sjulian			etfp->packets_out++;
42485722Sjulian		}
42585722Sjulian	}
42685722Sjulian	return (error);
42785722Sjulian}
42885722Sjulian
42985722Sjulian/*
43085722Sjulian * Do local shutdown processing..
43185722Sjulian * All our links and the name have already been removed.
43285722Sjulian */
43385722Sjulianstatic int
43485722Sjulianng_etf_shutdown(node_p node)
43585722Sjulian{
43685722Sjulian	const etf_p privdata = NG_NODE_PRIVATE(node);
43785722Sjulian
43885722Sjulian	NG_NODE_SET_PRIVATE(node, NULL);
43985722Sjulian	NG_NODE_UNREF(privdata->node);
440184205Sdes	free(privdata, M_NETGRAPH_ETF);
44185722Sjulian	return (0);
44285722Sjulian}
44385722Sjulian
44485722Sjulian/*
44585722Sjulian * Hook disconnection
44685722Sjulian *
44785722Sjulian * For this type, removal of the last link destroys the node
44885722Sjulian */
44985722Sjulianstatic int
45085722Sjulianng_etf_disconnect(hook_p hook)
45185722Sjulian{
45285722Sjulian	const etf_p etfp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
45385722Sjulian	int i;
454123670Sru	struct filter *fil1, *fil2;
45585722Sjulian
45685722Sjulian	/* purge any rules that refer to this filter */
45785722Sjulian	for (i = 0; i < HASHSIZE; i++) {
458123670Sru		fil1 = LIST_FIRST(&etfp->hashtable[i]);
459123670Sru		while (fil1 != NULL) {
460123670Sru			fil2 = LIST_NEXT(fil1, next);
461123670Sru			if (fil1->match_hook == hook) {
462123670Sru				LIST_REMOVE(fil1, next);
463184205Sdes				free(fil1, M_NETGRAPH_ETF);
46485722Sjulian			}
465123670Sru			fil1 = fil2;
46685722Sjulian		}
46785722Sjulian	}
46885722Sjulian
46985722Sjulian	/* If it's not one of the special hooks, then free it */
47085722Sjulian	if (hook == etfp->downstream_hook.hook) {
47185722Sjulian		etfp->downstream_hook.hook = NULL;
47285722Sjulian	} else if (hook == etfp->nomatch_hook.hook) {
47385722Sjulian		etfp->nomatch_hook.hook = NULL;
47485722Sjulian	} else {
47585722Sjulian		if (NG_HOOK_PRIVATE(hook)) /* Paranoia */
476184205Sdes			free(NG_HOOK_PRIVATE(hook), M_NETGRAPH_ETF);
47785722Sjulian	}
47885722Sjulian
47985722Sjulian	NG_HOOK_SET_PRIVATE(hook, NULL);
48085722Sjulian
48185722Sjulian	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
48285722Sjulian	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) /* already shutting down? */
48385722Sjulian		ng_rmnode_self(NG_HOOK_NODE(hook));
48485722Sjulian	return (0);
48585722Sjulian}
48685722Sjulian
487