1/*-
2 * Copyright (c) 2001-2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * 	All rights reserved.
5 *
6 * Author: Hartmut Brandt <harti@freebsd.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * Netgraph module for ATM-Forum UNI 4.0 signalling
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD$");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/malloc.h>
39#include <sys/mbuf.h>
40#include <sys/errno.h>
41#include <sys/syslog.h>
42#include <sys/socket.h>
43#include <sys/socketvar.h>
44#include <sys/callout.h>
45#include <sys/sbuf.h>
46#include <machine/stdarg.h>
47
48#include <netgraph/ng_message.h>
49#include <netgraph/netgraph.h>
50#include <netgraph/ng_parse.h>
51#include <netnatm/unimsg.h>
52#include <netnatm/msg/unistruct.h>
53#include <netgraph/atm/ngatmbase.h>
54#include <netnatm/saal/sscopdef.h>
55#include <netnatm/saal/sscfudef.h>
56#include <netgraph/atm/uni/ng_uni_cust.h>
57#include <netnatm/sig/uni.h>
58#include <netnatm/sig/unisig.h>
59#include <netgraph/atm/ng_sscop.h>
60#include <netgraph/atm/ng_sscfu.h>
61#include <netgraph/atm/ng_uni.h>
62
63static MALLOC_DEFINE(M_NG_UNI, "netgraph_uni_node", "netgraph uni node");
64static MALLOC_DEFINE(M_UNI, "netgraph_uni_data", "uni protocol data");
65
66MODULE_DEPEND(ng_uni, ngatmbase, 1, 1, 1);
67
68/*
69 * Private node data
70 */
71struct priv {
72	hook_p	upper;
73	hook_p	lower;
74	struct uni *uni;
75	int	enabled;
76};
77
78/* UNI CONFIG MASK */
79static const struct ng_parse_struct_field ng_uni_config_mask_type_info[] =
80	NGM_UNI_CONFIG_MASK_INFO;
81static const struct ng_parse_type ng_uni_config_mask_type = {
82	&ng_parse_struct_type,
83	ng_uni_config_mask_type_info
84};
85
86/* UNI_CONFIG */
87static const struct ng_parse_struct_field ng_uni_config_type_info[] =
88	NGM_UNI_CONFIG_INFO;
89static const struct ng_parse_type ng_uni_config_type = {
90	&ng_parse_struct_type,
91	ng_uni_config_type_info
92};
93
94/* SET CONFIG */
95static const struct ng_parse_struct_field ng_uni_set_config_type_info[] =
96	NGM_UNI_SET_CONFIG_INFO;
97static const struct ng_parse_type ng_uni_set_config_type = {
98	&ng_parse_struct_type,
99	ng_uni_set_config_type_info
100};
101
102/*
103 * Parse DEBUG
104 */
105static const struct ng_parse_fixedarray_info ng_uni_debuglevel_type_info =
106    NGM_UNI_DEBUGLEVEL_INFO;
107static const struct ng_parse_type ng_uni_debuglevel_type = {
108	&ng_parse_fixedarray_type,
109	&ng_uni_debuglevel_type_info
110};
111static const struct ng_parse_struct_field ng_uni_debug_type_info[] =
112    NGM_UNI_DEBUG_INFO;
113static const struct ng_parse_type ng_uni_debug_type = {
114	&ng_parse_struct_type,
115	ng_uni_debug_type_info
116};
117
118/*
119 * Command list
120 */
121static const struct ng_cmdlist ng_uni_cmdlist[] = {
122	{
123	  NGM_UNI_COOKIE,
124	  NGM_UNI_GETDEBUG,
125	  "getdebug",
126	  NULL,
127	  &ng_uni_debug_type
128	},
129	{
130	  NGM_UNI_COOKIE,
131	  NGM_UNI_SETDEBUG,
132	  "setdebug",
133	  &ng_uni_debug_type,
134	  NULL
135	},
136	{
137	  NGM_UNI_COOKIE,
138	  NGM_UNI_GET_CONFIG,
139	  "get_config",
140	  NULL,
141	  &ng_uni_config_type
142	},
143	{
144	  NGM_UNI_COOKIE,
145	  NGM_UNI_SET_CONFIG,
146	  "set_config",
147	  &ng_uni_set_config_type,
148	  &ng_uni_config_mask_type,
149	},
150	{
151	  NGM_UNI_COOKIE,
152	  NGM_UNI_ENABLE,
153	  "enable",
154	  NULL,
155	  NULL,
156	},
157	{
158	  NGM_UNI_COOKIE,
159	  NGM_UNI_DISABLE,
160	  "disable",
161	  NULL,
162	  NULL,
163	},
164	{
165	  NGM_UNI_COOKIE,
166	  NGM_UNI_GETSTATE,
167	  "getstate",
168	  NULL,
169	  &ng_parse_uint32_type
170	},
171	{ 0 }
172};
173
174/*
175 * Netgraph module data
176 */
177static ng_constructor_t ng_uni_constructor;
178static ng_shutdown_t	ng_uni_shutdown;
179static ng_rcvmsg_t	ng_uni_rcvmsg;
180static ng_newhook_t	ng_uni_newhook;
181static ng_disconnect_t	ng_uni_disconnect;
182static ng_rcvdata_t	ng_uni_rcvlower;
183static ng_rcvdata_t	ng_uni_rcvupper;
184
185static int ng_uni_mod_event(module_t, int, void *);
186
187static struct ng_type ng_uni_typestruct = {
188	.version =	NG_ABI_VERSION,
189	.name =		NG_UNI_NODE_TYPE,
190	.mod_event =	ng_uni_mod_event,
191	.constructor =	ng_uni_constructor,
192	.rcvmsg =	ng_uni_rcvmsg,
193	.shutdown =	ng_uni_shutdown,
194	.newhook =	ng_uni_newhook,
195	.rcvdata =	ng_uni_rcvlower,
196	.disconnect =	ng_uni_disconnect,
197	.cmdlist =	ng_uni_cmdlist,
198};
199NETGRAPH_INIT(uni, &ng_uni_typestruct);
200
201static void uni_uni_output(struct uni *, void *, enum uni_sig, u_int32_t,
202    struct uni_msg *);
203static void uni_saal_output(struct uni *, void *, enum saal_sig,
204    struct uni_msg *);
205static void uni_verbose(struct uni *, void *, u_int, const char *, ...)
206    __printflike(4, 5);
207static void uni_do_status(struct uni *, void *, void *, const char *, ...)
208    __printflike(4, 5);
209
210static const struct uni_funcs uni_funcs = {
211	uni_uni_output,
212	uni_saal_output,
213	uni_verbose,
214	uni_do_status
215};
216
217/************************************************************/
218/*
219 * NODE MANAGEMENT
220 */
221static int
222ng_uni_constructor(node_p node)
223{
224	struct priv *priv;
225
226	priv = malloc(sizeof(*priv), M_NG_UNI, M_WAITOK | M_ZERO);
227
228	if ((priv->uni = uni_create(node, &uni_funcs)) == NULL) {
229		free(priv, M_NG_UNI);
230		return (ENOMEM);
231	}
232
233	NG_NODE_SET_PRIVATE(node, priv);
234	NG_NODE_FORCE_WRITER(node);
235
236	return (0);
237}
238
239static int
240ng_uni_shutdown(node_p node)
241{
242	struct priv *priv = NG_NODE_PRIVATE(node);
243
244	uni_destroy(priv->uni);
245
246	free(priv, M_NG_UNI);
247	NG_NODE_SET_PRIVATE(node, NULL);
248
249	NG_NODE_UNREF(node);
250
251	return (0);
252}
253
254/************************************************************/
255/*
256 * CONTROL MESSAGES
257 */
258static void
259uni_do_status(struct uni *uni, void *uarg, void *sbuf, const char *fmt, ...)
260{
261	va_list ap;
262
263	va_start(ap, fmt);
264	sbuf_printf(sbuf, fmt, ap);
265	va_end(ap);
266}
267
268static int
269text_status(node_p node, struct priv *priv, char *buf, u_int len)
270{
271	struct sbuf sbuf;
272	u_int f;
273
274	sbuf_new(&sbuf, buf, len, 0);
275
276	if (priv->lower != NULL)
277		sbuf_printf(&sbuf, "lower hook: connected to %s:%s\n",
278		    NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->lower))),
279		    NG_HOOK_NAME(NG_HOOK_PEER(priv->lower)));
280	else
281		sbuf_printf(&sbuf, "lower hook: <not connected>\n");
282
283	if (priv->upper != NULL)
284		sbuf_printf(&sbuf, "upper hook: connected to %s:%s\n",
285		    NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->upper))),
286		    NG_HOOK_NAME(NG_HOOK_PEER(priv->upper)));
287	else
288		sbuf_printf(&sbuf, "upper hook: <not connected>\n");
289
290	sbuf_printf(&sbuf, "debugging:");
291	for (f = 0; f < UNI_MAXFACILITY; f++)
292		if (uni_get_debug(priv->uni, f) != 0)
293			sbuf_printf(&sbuf, " %s=%u", uni_facname(f),
294			    uni_get_debug(priv->uni, f));
295	sbuf_printf(&sbuf, "\n");
296
297	if (priv->uni)
298		uni_status(priv->uni, &sbuf);
299
300	sbuf_finish(&sbuf);
301	return (sbuf_len(&sbuf));
302}
303
304static int
305ng_uni_rcvmsg(node_p node, item_p item, hook_p lasthook)
306{
307	struct priv *priv = NG_NODE_PRIVATE(node);
308	struct ng_mesg *resp = NULL;
309	struct ng_mesg *msg;
310	int error = 0;
311	u_int i;
312
313	NGI_GET_MSG(item, msg);
314
315	switch (msg->header.typecookie) {
316
317	  case NGM_GENERIC_COOKIE:
318		switch (msg->header.cmd) {
319
320		  case NGM_TEXT_STATUS:
321			NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
322			if (resp == NULL) {
323				error = ENOMEM;
324				break;
325			}
326
327			resp->header.arglen = text_status(node, priv,
328			    (char *)resp->data, resp->header.arglen) + 1;
329			break;
330
331		  default:
332			error = EINVAL;
333			break;
334		}
335		break;
336
337	  case NGM_UNI_COOKIE:
338		switch (msg->header.cmd) {
339
340		  case NGM_UNI_SETDEBUG:
341		    {
342			struct ngm_uni_debug *arg;
343
344			if (msg->header.arglen > sizeof(*arg)) {
345				error = EINVAL;
346				break;
347			}
348			arg = (struct ngm_uni_debug *)msg->data;
349			for (i = 0; i < UNI_MAXFACILITY; i++)
350				uni_set_debug(priv->uni, i, arg->level[i]);
351			break;
352		    }
353
354		  case NGM_UNI_GETDEBUG:
355		    {
356			struct ngm_uni_debug *arg;
357
358			NG_MKRESPONSE(resp, msg, sizeof(*arg), M_NOWAIT);
359			if(resp == NULL) {
360				error = ENOMEM;
361				break;
362			}
363			arg = (struct ngm_uni_debug *)resp->data;
364			for (i = 0; i < UNI_MAXFACILITY; i++)
365				arg->level[i] = uni_get_debug(priv->uni, i);
366			break;
367		    }
368
369		  case NGM_UNI_GET_CONFIG:
370		    {
371			struct uni_config *config;
372
373			if (msg->header.arglen != 0) {
374				error = EINVAL;
375				break;
376			}
377			NG_MKRESPONSE(resp, msg, sizeof(*config), M_NOWAIT);
378			if (resp == NULL) {
379				error = ENOMEM;
380				break;
381			}
382			config = (struct uni_config *)resp->data;
383			uni_get_config(priv->uni, config);
384
385			break;
386		    }
387
388		  case NGM_UNI_SET_CONFIG:
389		    {
390			struct ngm_uni_set_config *arg;
391			struct ngm_uni_config_mask *mask;
392
393			if (msg->header.arglen != sizeof(*arg)) {
394				error = EINVAL;
395				break;
396			}
397			arg = (struct ngm_uni_set_config *)msg->data;
398
399			NG_MKRESPONSE(resp, msg, sizeof(*mask), M_NOWAIT);
400			if (resp == NULL) {
401				error = ENOMEM;
402				break;
403			}
404			mask = (struct ngm_uni_config_mask *)resp->data;
405
406			*mask = arg->mask;
407
408			uni_set_config(priv->uni, &arg->config,
409			    &mask->mask, &mask->popt_mask, &mask->option_mask);
410
411			break;
412		    }
413
414		  case NGM_UNI_ENABLE:
415			if (msg->header.arglen != 0) {
416				error = EINVAL;
417				break;
418			}
419			if (priv->enabled) {
420				error = EISCONN;
421				break;
422			}
423			priv->enabled = 1;
424			break;
425
426		  case NGM_UNI_DISABLE:
427			if (msg->header.arglen != 0) {
428				error = EINVAL;
429				break;
430			}
431			if (!priv->enabled) {
432				error = ENOTCONN;
433				break;
434			}
435			priv->enabled = 0;
436			uni_reset(priv->uni);
437			break;
438
439		  case NGM_UNI_GETSTATE:
440			if (msg->header.arglen != 0) {
441				error = EINVAL;
442				break;
443			}
444			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
445			if(resp == NULL) {
446				error = ENOMEM;
447				break;
448			}
449			*(u_int32_t *)resp->data =
450			    priv->enabled ? (uni_getcustate(priv->uni) + 1)
451			                  : 0;
452			break;
453
454		  default:
455			error = EINVAL;
456			break;
457		}
458		break;
459
460	  default:
461		error = EINVAL;
462		break;
463	}
464
465	NG_RESPOND_MSG(error, node, item, resp);
466	NG_FREE_MSG(msg);
467	return (error);
468}
469
470/************************************************************/
471/*
472 * HOOK MANAGEMENT
473 */
474static int
475ng_uni_newhook(node_p node, hook_p hook, const char *name)
476{
477	struct priv *priv = NG_NODE_PRIVATE(node);
478
479	if (strcmp(name, "lower") == 0) {
480		priv->lower = hook;
481	} else if(strcmp(name, "upper") == 0) {
482		priv->upper = hook;
483		NG_HOOK_SET_RCVDATA(hook, ng_uni_rcvupper);
484	} else
485		return EINVAL;
486
487	return 0;
488}
489
490static int
491ng_uni_disconnect(hook_p hook)
492{
493	node_p node = NG_HOOK_NODE(hook);
494	struct priv *priv = NG_NODE_PRIVATE(node);
495
496	if(hook == priv->lower)
497		priv->lower = NULL;
498	else if(hook == priv->upper)
499		priv->upper = NULL;
500	else
501		printf("%s: bogus hook %s\n", __func__, NG_HOOK_NAME(hook));
502
503	if (NG_NODE_NUMHOOKS(node) == 0) {
504		if (NG_NODE_IS_VALID(node))
505			ng_rmnode_self(node);
506	}
507
508	return (0);
509}
510
511/************************************************************/
512/*
513 * DATA
514 */
515/*
516 * Receive signal from USER.
517 *
518 * Repackage the data into one large buffer.
519 */
520static int
521ng_uni_rcvupper(hook_p hook, item_p item)
522{
523	node_p node = NG_HOOK_NODE(hook);
524	struct priv *priv = NG_NODE_PRIVATE(node);
525	struct mbuf *m;
526	struct uni_arg arg;
527	struct uni_msg *msg;
528	int error;
529
530	if (!priv->enabled) {
531		NG_FREE_ITEM(item);
532		return (ENOTCONN);
533	}
534
535	NGI_GET_M(item, m);
536	NG_FREE_ITEM(item);
537
538	if ((error = uni_msg_unpack_mbuf(m, &msg)) != 0) {
539		m_freem(m);
540		return (error);
541	}
542	m_freem(m);
543
544	if (uni_msg_len(msg) < sizeof(arg)) {
545		printf("%s: packet too short\n", __func__);
546		uni_msg_destroy(msg);
547		return (EINVAL);
548	}
549
550	bcopy(msg->b_rptr, &arg, sizeof(arg));
551	msg->b_rptr += sizeof(arg);
552
553	if (arg.sig >= UNIAPI_MAXSIG) {
554		printf("%s: bogus signal\n", __func__);
555		uni_msg_destroy(msg);
556		return (EINVAL);
557	}
558	uni_uni_input(priv->uni, arg.sig, arg.cookie, msg);
559	uni_work(priv->uni);
560
561	return (0);
562}
563
564
565/*
566 * Upper layer signal from UNI
567 */
568static void
569uni_uni_output(struct uni *uni, void *varg, enum uni_sig sig, u_int32_t cookie,
570    struct uni_msg *msg)
571{
572	node_p node = (node_p)varg;
573	struct priv *priv = NG_NODE_PRIVATE(node);
574	struct mbuf *m;
575	struct uni_arg arg;
576	int error;
577
578	if (priv->upper == NULL) {
579		if (msg != NULL)
580			uni_msg_destroy(msg);
581		return;
582	}
583	arg.sig = sig;
584	arg.cookie = cookie;
585
586	m = uni_msg_pack_mbuf(msg, &arg, sizeof(arg));
587	if (msg != NULL)
588		uni_msg_destroy(msg);
589	if (m == NULL)
590		return;
591
592	NG_SEND_DATA_ONLY(error, priv->upper, m);
593}
594
595
596static void
597dump_uni_msg(struct uni_msg *msg)
598{
599	u_int pos;
600
601	for (pos = 0; pos < uni_msg_len(msg); pos++) {
602		if (pos % 16 == 0)
603			printf("%06o ", pos);
604		if (pos % 16 == 8)
605			printf("  ");
606		printf(" %02x", msg->b_rptr[pos]);
607		if (pos % 16 == 15)
608			printf("\n");
609	}
610	if (pos % 16 != 0)
611		printf("\n");
612}
613
614
615/*
616 * Dump a SAAL signal in either direction
617 */
618static void
619dump_saal_signal(node_p node, enum saal_sig sig, struct uni_msg *msg, int to)
620{
621	struct priv *priv = NG_NODE_PRIVATE(node);
622
623	printf("signal %s SAAL: ", to ? "to" : "from");
624
625	switch (sig) {
626
627#define D(S) case S: printf("%s", #S); break
628
629	D(SAAL_ESTABLISH_request);
630	D(SAAL_ESTABLISH_indication);
631	D(SAAL_ESTABLISH_confirm);
632	D(SAAL_RELEASE_request);
633	D(SAAL_RELEASE_confirm);
634	D(SAAL_RELEASE_indication);
635	D(SAAL_DATA_request);
636	D(SAAL_DATA_indication);
637	D(SAAL_UDATA_request);
638	D(SAAL_UDATA_indication);
639
640#undef D
641	  default:
642		printf("sig=%d", sig); break;
643	}
644	if (msg != NULL) {
645		printf(" data=%zu\n", uni_msg_len(msg));
646		if (uni_get_debug(priv->uni, UNI_FAC_SAAL) > 1)
647			dump_uni_msg(msg);
648	} else
649		printf("\n");
650}
651
652/*
653 * Receive signal from SSCOP.
654 *
655 * If this is a data signal, repackage the data into one large buffer.
656 * UNI shouldn't be the bottleneck in a system and this greatly simplifies
657 * parsing in UNI.
658 */
659static int
660ng_uni_rcvlower(hook_p hook __unused, item_p item)
661{
662	node_p node = NG_HOOK_NODE(hook);
663	struct priv *priv = NG_NODE_PRIVATE(node);
664	struct mbuf *m;
665	struct sscfu_arg arg;
666	struct uni_msg *msg;
667	int error;
668
669	if (!priv->enabled) {
670		NG_FREE_ITEM(item);
671		return (ENOTCONN);
672	}
673
674	NGI_GET_M(item, m);
675	NG_FREE_ITEM(item);
676
677	if ((error = uni_msg_unpack_mbuf(m, &msg)) != 0) {
678		m_freem(m);
679		return (error);
680	}
681	m_freem(m);
682
683	if (uni_msg_len(msg) < sizeof(arg)) {
684		uni_msg_destroy(msg);
685		printf("%s: packet too short\n", __func__);
686		return (EINVAL);
687	}
688	bcopy(msg->b_rptr, &arg, sizeof(arg));
689	msg->b_rptr += sizeof(arg);
690
691	if (arg.sig > SAAL_UDATA_indication) {
692		uni_msg_destroy(msg);
693		printf("%s: bogus signal\n", __func__);
694		return (EINVAL);
695	}
696
697	if (uni_get_debug(priv->uni, UNI_FAC_SAAL) > 0)
698		dump_saal_signal(node, arg.sig, msg, 0);
699
700	uni_saal_input(priv->uni, arg.sig, msg);
701	uni_work(priv->uni);
702
703	return (0);
704}
705
706/*
707 * Send signal to sscop.
708 * Pack the message into an mbuf chain.
709 */
710static void
711uni_saal_output(struct uni *uni, void *varg, enum saal_sig sig, struct uni_msg *msg)
712{
713	node_p node = (node_p)varg;
714	struct priv *priv = NG_NODE_PRIVATE(node);
715	struct mbuf *m;
716	struct sscfu_arg arg;
717	int error;
718
719	if (uni_get_debug(priv->uni, UNI_FAC_SAAL) > 0)
720		dump_saal_signal(node, sig, msg, 1);
721
722	if (priv->lower == NULL) {
723		if (msg != NULL)
724			uni_msg_destroy(msg);
725		return;
726	}
727
728	arg.sig = sig;
729
730	m = uni_msg_pack_mbuf(msg, &arg, sizeof(arg));
731	if (msg != NULL)
732		uni_msg_destroy(msg);
733	if (m == NULL)
734		return;
735
736	NG_SEND_DATA_ONLY(error, priv->lower, m);
737}
738
739static void
740uni_verbose(struct uni *uni, void *varg, u_int fac, const char *fmt, ...)
741{
742	va_list ap;
743
744	static char *facnames[] = {
745#define UNI_DEBUG_DEFINE(D) [UNI_FAC_##D] = #D,
746		UNI_DEBUG_FACILITIES
747#undef UNI_DEBUG_DEFINE
748	};
749
750	printf("%s: ", facnames[fac]);
751
752	va_start(ap, fmt);
753	vprintf(fmt, ap);
754	va_end(ap);
755
756	printf("\n");
757}
758
759
760/************************************************************/
761/*
762 * Memory debugging
763 */
764struct unimem_debug {
765	const char	*file;
766	u_int		lno;
767	LIST_ENTRY(unimem_debug) link;
768	char		data[0];
769};
770LIST_HEAD(unimem_debug_list, unimem_debug);
771
772static struct unimem_debug_list nguni_freemem[UNIMEM_TYPES] = {
773    LIST_HEAD_INITIALIZER(nguni_freemem[0]),
774    LIST_HEAD_INITIALIZER(nguni_freemem[1]),
775    LIST_HEAD_INITIALIZER(nguni_freemem[2]),
776    LIST_HEAD_INITIALIZER(nguni_freemem[3]),
777    LIST_HEAD_INITIALIZER(nguni_freemem[4]),
778};
779static struct unimem_debug_list nguni_usedmem[UNIMEM_TYPES] = {
780    LIST_HEAD_INITIALIZER(nguni_usedmem[0]),
781    LIST_HEAD_INITIALIZER(nguni_usedmem[1]),
782    LIST_HEAD_INITIALIZER(nguni_usedmem[2]),
783    LIST_HEAD_INITIALIZER(nguni_usedmem[3]),
784    LIST_HEAD_INITIALIZER(nguni_usedmem[4]),
785};
786
787static struct mtx nguni_unilist_mtx;
788
789static const char *unimem_names[UNIMEM_TYPES] = {
790	"instance",
791	"all",
792	"signal",
793	"call",
794	"party"
795};
796
797static void
798uni_init(void)
799{
800	mtx_init(&nguni_unilist_mtx, "netgraph UNI structure lists", NULL,
801	    MTX_DEF);
802}
803
804static void
805uni_fini(void)
806{
807	u_int type;
808	struct unimem_debug *h;
809
810	for (type = 0; type < UNIMEM_TYPES; type++) {
811		while ((h = LIST_FIRST(&nguni_freemem[type])) != NULL) {
812			LIST_REMOVE(h, link);
813			free(h, M_UNI);
814		}
815
816		while ((h = LIST_FIRST(&nguni_usedmem[type])) != NULL) {
817			LIST_REMOVE(h, link);
818			printf("ng_uni: %s in use: %p (%s,%u)\n",
819			    unimem_names[type], (caddr_t)h->data,
820			    h->file, h->lno);
821			free(h, M_UNI);
822		}
823	}
824
825	mtx_destroy(&nguni_unilist_mtx);
826}
827
828/*
829 * Allocate a chunk of memory from a given type.
830 */
831void *
832ng_uni_malloc(enum unimem type, const char *file, u_int lno)
833{
834	struct unimem_debug *d;
835	size_t full;
836
837	/*
838	 * Try to allocate
839	 */
840	mtx_lock(&nguni_unilist_mtx);
841	if ((d = LIST_FIRST(&nguni_freemem[type])) != NULL)
842		LIST_REMOVE(d, link);
843	mtx_unlock(&nguni_unilist_mtx);
844
845	if (d == NULL) {
846		/*
847		 * allocate
848		 */
849		full = unimem_sizes[type] + offsetof(struct unimem_debug, data);
850		if ((d = malloc(full, M_UNI, M_NOWAIT | M_ZERO)) == NULL)
851			return (NULL);
852	} else {
853		bzero(d->data, unimem_sizes[type]);
854	}
855	d->file = file;
856	d->lno = lno;
857
858	mtx_lock(&nguni_unilist_mtx);
859	LIST_INSERT_HEAD(&nguni_usedmem[type], d, link);
860	mtx_unlock(&nguni_unilist_mtx);
861	return (d->data);
862}
863
864void
865ng_uni_free(enum unimem type, void *ptr, const char *file, u_int lno)
866{
867	struct unimem_debug *d, *h;
868
869	d = (struct unimem_debug *)
870	    ((char *)ptr - offsetof(struct unimem_debug, data));
871
872	mtx_lock(&nguni_unilist_mtx);
873
874	LIST_FOREACH(h, &nguni_usedmem[type], link)
875		if (d == h)
876			break;
877
878	if (h != NULL) {
879		LIST_REMOVE(d, link);
880		LIST_INSERT_HEAD(&nguni_freemem[type], d, link);
881	} else {
882		/*
883		 * Not on used list - try free list.
884		 */
885		LIST_FOREACH(h, &nguni_freemem[type], link)
886			if (d == h)
887				break;
888		if (h == NULL)
889			printf("ng_uni: %s,%u: %p(%s) was never allocated\n",
890			    file, lno, ptr, unimem_names[type]);
891		else
892			printf("ng_uni: %s,%u: %p(%s) was already destroyed "
893			    "in %s,%u\n",
894			    file, lno, ptr, unimem_names[type],
895			    h->file, h->lno);
896	}
897	mtx_unlock(&nguni_unilist_mtx);
898}
899/************************************************************/
900/*
901 * INITIALISATION
902 */
903
904/*
905 * Loading and unloading of node type
906 */
907static int
908ng_uni_mod_event(module_t mod, int event, void *data)
909{
910	int error = 0;
911
912	switch(event) {
913
914	  case MOD_LOAD:
915		uni_init();
916		break;
917
918	  case MOD_UNLOAD:
919		uni_fini();
920		break;
921
922	  default:
923		error = EOPNOTSUPP;
924		break;
925	}
926	return (error);
927}
928