freebsd-dev/sys/netgraph/atm/uni/ng_uni.c
2005-01-07 01:45:51 +00:00

932 lines
19 KiB
C

/*-
* Copyright (c) 2001-2003
* Fraunhofer Institute for Open Communication Systems (FhG Fokus).
* All rights reserved.
*
* Author: Hartmut Brandt <harti@freebsd.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Netgraph module for ATM-Forum UNI 4.0 signalling
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/errno.h>
#include <sys/syslog.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/callout.h>
#include <sys/sbuf.h>
#include <machine/stdarg.h>
#include <netgraph/ng_message.h>
#include <netgraph/netgraph.h>
#include <netgraph/ng_parse.h>
#include <netnatm/unimsg.h>
#include <netnatm/msg/unistruct.h>
#include <netgraph/atm/ngatmbase.h>
#include <netnatm/saal/sscopdef.h>
#include <netnatm/saal/sscfudef.h>
#include <netgraph/atm/uni/ng_uni_cust.h>
#include <netnatm/sig/uni.h>
#include <netnatm/sig/unisig.h>
#include <netgraph/atm/ng_sscop.h>
#include <netgraph/atm/ng_sscfu.h>
#include <netgraph/atm/ng_uni.h>
MALLOC_DEFINE(M_NG_UNI, "netgraph_uni", "netgraph uni node");
MALLOC_DEFINE(M_UNI, "netgraph uni", "uni protocol data");
MODULE_DEPEND(ng_uni, ngatmbase, 1, 1, 1);
/*
* Private node data
*/
struct priv {
hook_p upper;
hook_p lower;
struct uni *uni;
int enabled;
};
/* UNI CONFIG MASK */
static const struct ng_parse_struct_field ng_uni_config_mask_type_info[] =
NGM_UNI_CONFIG_MASK_INFO;
static const struct ng_parse_type ng_uni_config_mask_type = {
&ng_parse_struct_type,
ng_uni_config_mask_type_info
};
/* UNI_CONFIG */
static const struct ng_parse_struct_field ng_uni_config_type_info[] =
NGM_UNI_CONFIG_INFO;
static const struct ng_parse_type ng_uni_config_type = {
&ng_parse_struct_type,
ng_uni_config_type_info
};
/* SET CONFIG */
static const struct ng_parse_struct_field ng_uni_set_config_type_info[] =
NGM_UNI_SET_CONFIG_INFO;
static const struct ng_parse_type ng_uni_set_config_type = {
&ng_parse_struct_type,
ng_uni_set_config_type_info
};
/*
* Parse DEBUG
*/
static const struct ng_parse_fixedarray_info ng_uni_debuglevel_type_info =
NGM_UNI_DEBUGLEVEL_INFO;
static const struct ng_parse_type ng_uni_debuglevel_type = {
&ng_parse_fixedarray_type,
&ng_uni_debuglevel_type_info
};
static const struct ng_parse_struct_field ng_uni_debug_type_info[] =
NGM_UNI_DEBUG_INFO;
static const struct ng_parse_type ng_uni_debug_type = {
&ng_parse_struct_type,
ng_uni_debug_type_info
};
/*
* Command list
*/
static const struct ng_cmdlist ng_uni_cmdlist[] = {
{
NGM_UNI_COOKIE,
NGM_UNI_GETDEBUG,
"getdebug",
NULL,
&ng_uni_debug_type
},
{
NGM_UNI_COOKIE,
NGM_UNI_SETDEBUG,
"setdebug",
&ng_uni_debug_type,
NULL
},
{
NGM_UNI_COOKIE,
NGM_UNI_GET_CONFIG,
"get_config",
NULL,
&ng_uni_config_type
},
{
NGM_UNI_COOKIE,
NGM_UNI_SET_CONFIG,
"set_config",
&ng_uni_set_config_type,
&ng_uni_config_mask_type,
},
{
NGM_UNI_COOKIE,
NGM_UNI_ENABLE,
"enable",
NULL,
NULL,
},
{
NGM_UNI_COOKIE,
NGM_UNI_DISABLE,
"disable",
NULL,
NULL,
},
{
NGM_UNI_COOKIE,
NGM_UNI_GETSTATE,
"getstate",
NULL,
&ng_parse_uint32_type
},
{ 0 }
};
/*
* Netgraph module data
*/
static ng_constructor_t ng_uni_constructor;
static ng_shutdown_t ng_uni_shutdown;
static ng_rcvmsg_t ng_uni_rcvmsg;
static ng_newhook_t ng_uni_newhook;
static ng_disconnect_t ng_uni_disconnect;
static ng_rcvdata_t ng_uni_rcvlower;
static ng_rcvdata_t ng_uni_rcvupper;
static int ng_uni_mod_event(module_t, int, void *);
static struct ng_type ng_uni_typestruct = {
.version = NG_ABI_VERSION,
.name = NG_UNI_NODE_TYPE,
.mod_event = ng_uni_mod_event,
.constructor = ng_uni_constructor,
.rcvmsg = ng_uni_rcvmsg,
.shutdown = ng_uni_shutdown,
.newhook = ng_uni_newhook,
.rcvdata = ng_uni_rcvlower,
.disconnect = ng_uni_disconnect,
.cmdlist = ng_uni_cmdlist,
};
NETGRAPH_INIT(uni, &ng_uni_typestruct);
static void uni_uni_output(struct uni *, void *, enum uni_sig, u_int32_t,
struct uni_msg *);
static void uni_saal_output(struct uni *, void *, enum saal_sig,
struct uni_msg *);
static void uni_verbose(struct uni *, void *, u_int, const char *, ...)
__printflike(4, 5);
static void uni_do_status(struct uni *, void *, void *, const char *, ...)
__printflike(4, 5);
static const struct uni_funcs uni_funcs = {
uni_uni_output,
uni_saal_output,
uni_verbose,
uni_do_status
};
/************************************************************/
/*
* NODE MANAGEMENT
*/
static int
ng_uni_constructor(node_p node)
{
struct priv *priv;
if ((priv = malloc(sizeof(*priv), M_NG_UNI, M_NOWAIT | M_ZERO)) == NULL)
return (ENOMEM);
if ((priv->uni = uni_create(node, &uni_funcs)) == NULL) {
free(priv, M_NG_UNI);
return (ENOMEM);
}
NG_NODE_SET_PRIVATE(node, priv);
NG_NODE_FORCE_WRITER(node);
return (0);
}
static int
ng_uni_shutdown(node_p node)
{
struct priv *priv = NG_NODE_PRIVATE(node);
uni_destroy(priv->uni);
free(priv, M_NG_UNI);
NG_NODE_SET_PRIVATE(node, NULL);
NG_NODE_UNREF(node);
return (0);
}
/************************************************************/
/*
* CONTROL MESSAGES
*/
static void
uni_do_status(struct uni *uni, void *uarg, void *sbuf, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
sbuf_printf(sbuf, fmt, ap);
va_end(ap);
}
static int
text_status(node_p node, struct priv *priv, char *buf, u_int len)
{
struct sbuf sbuf;
u_int f;
sbuf_new(&sbuf, buf, len, 0);
if (priv->lower != NULL)
sbuf_printf(&sbuf, "lower hook: connected to %s:%s\n",
NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->lower))),
NG_HOOK_NAME(NG_HOOK_PEER(priv->lower)));
else
sbuf_printf(&sbuf, "lower hook: <not connected>\n");
if (priv->upper != NULL)
sbuf_printf(&sbuf, "upper hook: connected to %s:%s\n",
NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->upper))),
NG_HOOK_NAME(NG_HOOK_PEER(priv->upper)));
else
sbuf_printf(&sbuf, "upper hook: <not connected>\n");
sbuf_printf(&sbuf, "debugging:");
for (f = 0; f < UNI_MAXFACILITY; f++)
if (uni_get_debug(priv->uni, f) != 0)
sbuf_printf(&sbuf, " %s=%u", uni_facname(f),
uni_get_debug(priv->uni, f));
sbuf_printf(&sbuf, "\n");
if (priv->uni)
uni_status(priv->uni, &sbuf);
sbuf_finish(&sbuf);
return (sbuf_len(&sbuf));
}
static int
ng_uni_rcvmsg(node_p node, item_p item, hook_p lasthook)
{
struct priv *priv = NG_NODE_PRIVATE(node);
struct ng_mesg *resp = NULL;
struct ng_mesg *msg;
int error = 0;
u_int i;
NGI_GET_MSG(item, msg);
switch (msg->header.typecookie) {
case NGM_GENERIC_COOKIE:
switch (msg->header.cmd) {
case NGM_TEXT_STATUS:
NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
if (resp == NULL) {
error = ENOMEM;
break;
}
resp->header.arglen = text_status(node, priv,
(char *)resp->data, resp->header.arglen) + 1;
break;
default:
error = EINVAL;
break;
}
break;
case NGM_UNI_COOKIE:
switch (msg->header.cmd) {
case NGM_UNI_SETDEBUG:
{
struct ngm_uni_debug *arg;
if (msg->header.arglen > sizeof(*arg)) {
error = EINVAL;
break;
}
arg = (struct ngm_uni_debug *)msg->data;
for (i = 0; i < UNI_MAXFACILITY; i++)
uni_set_debug(priv->uni, i, arg->level[i]);
break;
}
case NGM_UNI_GETDEBUG:
{
struct ngm_uni_debug *arg;
NG_MKRESPONSE(resp, msg, sizeof(*arg), M_NOWAIT);
if(resp == NULL) {
error = ENOMEM;
break;
}
arg = (struct ngm_uni_debug *)resp->data;
for (i = 0; i < UNI_MAXFACILITY; i++)
arg->level[i] = uni_get_debug(priv->uni, i);
break;
}
case NGM_UNI_GET_CONFIG:
{
struct uni_config *config;
if (msg->header.arglen != 0) {
error = EINVAL;
break;
}
NG_MKRESPONSE(resp, msg, sizeof(*config), M_NOWAIT);
if (resp == NULL) {
error = ENOMEM;
break;
}
config = (struct uni_config *)resp->data;
uni_get_config(priv->uni, config);
break;
}
case NGM_UNI_SET_CONFIG:
{
struct ngm_uni_set_config *arg;
struct ngm_uni_config_mask *mask;
if (msg->header.arglen != sizeof(*arg)) {
error = EINVAL;
break;
}
arg = (struct ngm_uni_set_config *)msg->data;
NG_MKRESPONSE(resp, msg, sizeof(*mask), M_NOWAIT);
if (resp == NULL) {
error = ENOMEM;
break;
}
mask = (struct ngm_uni_config_mask *)resp->data;
*mask = arg->mask;
uni_set_config(priv->uni, &arg->config,
&mask->mask, &mask->popt_mask, &mask->option_mask);
break;
}
case NGM_UNI_ENABLE:
if (msg->header.arglen != 0) {
error = EINVAL;
break;
}
if (priv->enabled) {
error = EISCONN;
break;
}
priv->enabled = 1;
break;
case NGM_UNI_DISABLE:
if (msg->header.arglen != 0) {
error = EINVAL;
break;
}
if (!priv->enabled) {
error = ENOTCONN;
break;
}
priv->enabled = 0;
uni_reset(priv->uni);
break;
case NGM_UNI_GETSTATE:
if (msg->header.arglen != 0) {
error = EINVAL;
break;
}
NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
if(resp == NULL) {
error = ENOMEM;
break;
}
*(u_int32_t *)resp->data =
priv->enabled ? (uni_getcustate(priv->uni) + 1)
: 0;
break;
default:
error = EINVAL;
break;
}
break;
default:
error = EINVAL;
break;
}
NG_RESPOND_MSG(error, node, item, resp);
NG_FREE_MSG(msg);
return (error);
}
/************************************************************/
/*
* HOOK MANAGEMENT
*/
static int
ng_uni_newhook(node_p node, hook_p hook, const char *name)
{
struct priv *priv = NG_NODE_PRIVATE(node);
if (strcmp(name, "lower") == 0) {
priv->lower = hook;
} else if(strcmp(name, "upper") == 0) {
priv->upper = hook;
NG_HOOK_SET_RCVDATA(hook, ng_uni_rcvupper);
} else
return EINVAL;
return 0;
}
static int
ng_uni_disconnect(hook_p hook)
{
node_p node = NG_HOOK_NODE(hook);
struct priv *priv = NG_NODE_PRIVATE(node);
if(hook == priv->lower)
priv->lower = NULL;
else if(hook == priv->upper)
priv->upper = NULL;
else
printf("%s: bogus hook %s\n", __FUNCTION__, NG_HOOK_NAME(hook));
if (NG_NODE_NUMHOOKS(node) == 0) {
if (NG_NODE_IS_VALID(node))
ng_rmnode_self(node);
}
return (0);
}
/************************************************************/
/*
* DATA
*/
/*
* Receive signal from USER.
*
* Repackage the data into one large buffer.
*/
static int
ng_uni_rcvupper(hook_p hook, item_p item)
{
node_p node = NG_HOOK_NODE(hook);
struct priv *priv = NG_NODE_PRIVATE(node);
struct mbuf *m;
struct uni_arg arg;
struct uni_msg *msg;
int error;
if (!priv->enabled) {
NG_FREE_ITEM(item);
return (ENOTCONN);
}
NGI_GET_M(item, m);
NG_FREE_ITEM(item);
if ((error = uni_msg_unpack_mbuf(m, &msg)) != 0) {
m_freem(m);
return (error);
}
m_freem(m);
if (uni_msg_len(msg) < sizeof(arg)) {
printf("%s: packet too short\n", __FUNCTION__);
uni_msg_destroy(msg);
return (EINVAL);
}
bcopy(msg->b_rptr, &arg, sizeof(arg));
msg->b_rptr += sizeof(arg);
if (arg.sig >= UNIAPI_MAXSIG) {
printf("%s: bogus signal\n", __FUNCTION__);
uni_msg_destroy(msg);
return (EINVAL);
}
uni_uni_input(priv->uni, arg.sig, arg.cookie, msg);
uni_work(priv->uni);
return (0);
}
/*
* Upper layer signal from UNI
*/
static void
uni_uni_output(struct uni *uni, void *varg, enum uni_sig sig, u_int32_t cookie,
struct uni_msg *msg)
{
node_p node = (node_p)varg;
struct priv *priv = NG_NODE_PRIVATE(node);
struct mbuf *m;
struct uni_arg arg;
int error;
if (priv->upper == NULL) {
if (msg != NULL)
uni_msg_destroy(msg);
return;
}
arg.sig = sig;
arg.cookie = cookie;
m = uni_msg_pack_mbuf(msg, &arg, sizeof(arg));
if (msg != NULL)
uni_msg_destroy(msg);
if (m == NULL)
return;
NG_SEND_DATA_ONLY(error, priv->upper, m);
}
static void
dump_uni_msg(struct uni_msg *msg)
{
u_int pos;
for (pos = 0; pos < uni_msg_len(msg); pos++) {
if (pos % 16 == 0)
printf("%06o ", pos);
if (pos % 16 == 8)
printf(" ");
printf(" %02x", msg->b_rptr[pos]);
if (pos % 16 == 15)
printf("\n");
}
if (pos % 16 != 0)
printf("\n");
}
/*
* Dump a SAAL signal in either direction
*/
static void
dump_saal_signal(node_p node, enum saal_sig sig, struct uni_msg *msg, int to)
{
struct priv *priv = NG_NODE_PRIVATE(node);
printf("signal %s SAAL: ", to ? "to" : "from");
switch (sig) {
#define D(S) case S: printf("%s", #S); break
D(SAAL_ESTABLISH_request);
D(SAAL_ESTABLISH_indication);
D(SAAL_ESTABLISH_confirm);
D(SAAL_RELEASE_request);
D(SAAL_RELEASE_confirm);
D(SAAL_RELEASE_indication);
D(SAAL_DATA_request);
D(SAAL_DATA_indication);
D(SAAL_UDATA_request);
D(SAAL_UDATA_indication);
#undef D
default:
printf("sig=%d", sig); break;
}
if (msg != NULL) {
printf(" data=%zu\n", uni_msg_len(msg));
if (uni_get_debug(priv->uni, UNI_FAC_SAAL) > 1)
dump_uni_msg(msg);
} else
printf("\n");
}
/*
* Receive signal from SSCOP.
*
* If this is a data signal, repackage the data into one large buffer.
* UNI shouldn't be the bottleneck in a system and this greatly simplifies
* parsing in UNI.
*/
static int
ng_uni_rcvlower(hook_p hook __unused, item_p item)
{
node_p node = NG_HOOK_NODE(hook);
struct priv *priv = NG_NODE_PRIVATE(node);
struct mbuf *m;
struct sscfu_arg arg;
struct uni_msg *msg;
int error;
if (!priv->enabled) {
NG_FREE_ITEM(item);
return (ENOTCONN);
}
NGI_GET_M(item, m);
NG_FREE_ITEM(item);
if ((error = uni_msg_unpack_mbuf(m, &msg)) != 0) {
m_freem(m);
return (error);
}
m_freem(m);
if (uni_msg_len(msg) < sizeof(arg)) {
uni_msg_destroy(msg);
printf("%s: packet too short\n", __FUNCTION__);
return (EINVAL);
}
bcopy(msg->b_rptr, &arg, sizeof(arg));
msg->b_rptr += sizeof(arg);
if (arg.sig > SAAL_UDATA_indication) {
uni_msg_destroy(msg);
printf("%s: bogus signal\n", __FUNCTION__);
return (EINVAL);
}
if (uni_get_debug(priv->uni, UNI_FAC_SAAL) > 0)
dump_saal_signal(node, arg.sig, msg, 0);
uni_saal_input(priv->uni, arg.sig, msg);
uni_work(priv->uni);
return (0);
}
/*
* Send signal to sscop.
* Pack the message into an mbuf chain.
*/
static void
uni_saal_output(struct uni *uni, void *varg, enum saal_sig sig, struct uni_msg *msg)
{
node_p node = (node_p)varg;
struct priv *priv = NG_NODE_PRIVATE(node);
struct mbuf *m;
struct sscfu_arg arg;
int error;
if (uni_get_debug(priv->uni, UNI_FAC_SAAL) > 0)
dump_saal_signal(node, sig, msg, 1);
if (priv->lower == NULL) {
if (msg != NULL)
uni_msg_destroy(msg);
return;
}
arg.sig = sig;
m = uni_msg_pack_mbuf(msg, &arg, sizeof(arg));
if (msg != NULL)
uni_msg_destroy(msg);
if (m == NULL)
return;
NG_SEND_DATA_ONLY(error, priv->lower, m);
}
static void
uni_verbose(struct uni *uni, void *varg, u_int fac, const char *fmt, ...)
{
va_list ap;
static char *facnames[] = {
#define UNI_DEBUG_DEFINE(D) [UNI_FAC_##D] #D,
UNI_DEBUG_FACILITIES
#undef UNI_DEBUG_DEFINE
};
printf("%s: ", facnames[fac]);
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
printf("\n");
}
/************************************************************/
/*
* Memory debugging
*/
struct unimem_debug {
const char *file;
u_int lno;
LIST_ENTRY(unimem_debug) link;
char data[0];
};
LIST_HEAD(unimem_debug_list, unimem_debug);
static struct unimem_debug_list nguni_freemem[UNIMEM_TYPES] = {
LIST_HEAD_INITIALIZER(unimem_debug),
LIST_HEAD_INITIALIZER(unimem_debug),
LIST_HEAD_INITIALIZER(unimem_debug),
LIST_HEAD_INITIALIZER(unimem_debug),
LIST_HEAD_INITIALIZER(unimem_debug),
};
static struct unimem_debug_list nguni_usedmem[UNIMEM_TYPES] = {
LIST_HEAD_INITIALIZER(unimem_debug),
LIST_HEAD_INITIALIZER(unimem_debug),
LIST_HEAD_INITIALIZER(unimem_debug),
LIST_HEAD_INITIALIZER(unimem_debug),
LIST_HEAD_INITIALIZER(unimem_debug),
};
static struct mtx nguni_unilist_mtx;
static const char *unimem_names[UNIMEM_TYPES] = {
"instance",
"all",
"signal",
"call",
"party"
};
static void
uni_init(void)
{
mtx_init(&nguni_unilist_mtx, "netgraph UNI structure lists", NULL,
MTX_DEF);
}
static void
uni_fini(void)
{
u_int type;
struct unimem_debug *h;
for (type = 0; type < UNIMEM_TYPES; type++) {
while ((h = LIST_FIRST(&nguni_freemem[type])) != NULL) {
LIST_REMOVE(h, link);
free(h, M_UNI);
}
while ((h = LIST_FIRST(&nguni_usedmem[type])) != NULL) {
LIST_REMOVE(h, link);
printf("ng_uni: %s in use: %p (%s,%u)\n",
unimem_names[type], (caddr_t)h->data,
h->file, h->lno);
free(h, M_UNI);
}
}
mtx_destroy(&nguni_unilist_mtx);
}
/*
* Allocate a chunk of memory from a given type.
*/
void *
ng_uni_malloc(enum unimem type, const char *file, u_int lno)
{
struct unimem_debug *d;
size_t full;
/*
* Try to allocate
*/
mtx_lock(&nguni_unilist_mtx);
if ((d = LIST_FIRST(&nguni_freemem[type])) != NULL)
LIST_REMOVE(d, link);
mtx_unlock(&nguni_unilist_mtx);
if (d == NULL) {
/*
* allocate
*/
full = unimem_sizes[type] + offsetof(struct unimem_debug, data);
if ((d = malloc(full, M_UNI, M_NOWAIT | M_ZERO)) == NULL)
return (NULL);
} else {
bzero(d->data, unimem_sizes[type]);
}
d->file = file;
d->lno = lno;
mtx_lock(&nguni_unilist_mtx);
LIST_INSERT_HEAD(&nguni_usedmem[type], d, link);
mtx_unlock(&nguni_unilist_mtx);
return (d->data);
}
void
ng_uni_free(enum unimem type, void *ptr, const char *file, u_int lno)
{
struct unimem_debug *d, *h;
d = (struct unimem_debug *)
((char *)ptr - offsetof(struct unimem_debug, data));
mtx_lock(&nguni_unilist_mtx);
LIST_FOREACH(h, &nguni_usedmem[type], link)
if (d == h)
break;
if (h != NULL) {
LIST_REMOVE(d, link);
LIST_INSERT_HEAD(&nguni_freemem[type], d, link);
} else {
/*
* Not on used list - try free list.
*/
LIST_FOREACH(h, &nguni_freemem[type], link)
if (d == h)
break;
if (h == NULL)
printf("ng_uni: %s,%u: %p(%s) was never allocated\n",
file, lno, ptr, unimem_names[type]);
else
printf("ng_uni: %s,%u: %p(%s) was already destroyed "
"in %s,%u\n",
file, lno, ptr, unimem_names[type],
h->file, h->lno);
}
mtx_unlock(&nguni_unilist_mtx);
}
/************************************************************/
/*
* INITIALISATION
*/
/*
* Loading and unloading of node type
*/
static int
ng_uni_mod_event(module_t mod, int event, void *data)
{
int s;
int error = 0;
s = splnet();
switch(event) {
case MOD_LOAD:
uni_init();
break;
case MOD_UNLOAD:
uni_fini();
break;
default:
error = EOPNOTSUPP;
break;
}
splx(s);
return (error);
}