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