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	  case NGM_GENERIC_COOKIE:
319		switch (msg->header.cmd) {
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		  case NGM_UNI_SETDEBUG:
340		    {
341			struct ngm_uni_debug *arg;
342
343			if (msg->header.arglen > sizeof(*arg)) {
344				error = EINVAL;
345				break;
346			}
347			arg = (struct ngm_uni_debug *)msg->data;
348			for (i = 0; i < UNI_MAXFACILITY; i++)
349				uni_set_debug(priv->uni, i, arg->level[i]);
350			break;
351		    }
352
353		  case NGM_UNI_GETDEBUG:
354		    {
355			struct ngm_uni_debug *arg;
356
357			NG_MKRESPONSE(resp, msg, sizeof(*arg), M_NOWAIT);
358			if(resp == NULL) {
359				error = ENOMEM;
360				break;
361			}
362			arg = (struct ngm_uni_debug *)resp->data;
363			for (i = 0; i < UNI_MAXFACILITY; i++)
364				arg->level[i] = uni_get_debug(priv->uni, i);
365			break;
366		    }
367
368		  case NGM_UNI_GET_CONFIG:
369		    {
370			struct uni_config *config;
371
372			if (msg->header.arglen != 0) {
373				error = EINVAL;
374				break;
375			}
376			NG_MKRESPONSE(resp, msg, sizeof(*config), M_NOWAIT);
377			if (resp == NULL) {
378				error = ENOMEM;
379				break;
380			}
381			config = (struct uni_config *)resp->data;
382			uni_get_config(priv->uni, config);
383
384			break;
385		    }
386
387		  case NGM_UNI_SET_CONFIG:
388		    {
389			struct ngm_uni_set_config *arg;
390			struct ngm_uni_config_mask *mask;
391
392			if (msg->header.arglen != sizeof(*arg)) {
393				error = EINVAL;
394				break;
395			}
396			arg = (struct ngm_uni_set_config *)msg->data;
397
398			NG_MKRESPONSE(resp, msg, sizeof(*mask), M_NOWAIT);
399			if (resp == NULL) {
400				error = ENOMEM;
401				break;
402			}
403			mask = (struct ngm_uni_config_mask *)resp->data;
404
405			*mask = arg->mask;
406
407			uni_set_config(priv->uni, &arg->config,
408			    &mask->mask, &mask->popt_mask, &mask->option_mask);
409
410			break;
411		    }
412
413		  case NGM_UNI_ENABLE:
414			if (msg->header.arglen != 0) {
415				error = EINVAL;
416				break;
417			}
418			if (priv->enabled) {
419				error = EISCONN;
420				break;
421			}
422			priv->enabled = 1;
423			break;
424
425		  case NGM_UNI_DISABLE:
426			if (msg->header.arglen != 0) {
427				error = EINVAL;
428				break;
429			}
430			if (!priv->enabled) {
431				error = ENOTCONN;
432				break;
433			}
434			priv->enabled = 0;
435			uni_reset(priv->uni);
436			break;
437
438		  case NGM_UNI_GETSTATE:
439			if (msg->header.arglen != 0) {
440				error = EINVAL;
441				break;
442			}
443			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
444			if(resp == NULL) {
445				error = ENOMEM;
446				break;
447			}
448			*(u_int32_t *)resp->data =
449			    priv->enabled ? (uni_getcustate(priv->uni) + 1)
450			                  : 0;
451			break;
452
453		  default:
454			error = EINVAL;
455			break;
456		}
457		break;
458
459	  default:
460		error = EINVAL;
461		break;
462	}
463
464	NG_RESPOND_MSG(error, node, item, resp);
465	NG_FREE_MSG(msg);
466	return (error);
467}
468
469/************************************************************/
470/*
471 * HOOK MANAGEMENT
472 */
473static int
474ng_uni_newhook(node_p node, hook_p hook, const char *name)
475{
476	struct priv *priv = NG_NODE_PRIVATE(node);
477
478	if (strcmp(name, "lower") == 0) {
479		priv->lower = hook;
480	} else if(strcmp(name, "upper") == 0) {
481		priv->upper = hook;
482		NG_HOOK_SET_RCVDATA(hook, ng_uni_rcvupper);
483	} else
484		return EINVAL;
485
486	return 0;
487}
488
489static int
490ng_uni_disconnect(hook_p hook)
491{
492	node_p node = NG_HOOK_NODE(hook);
493	struct priv *priv = NG_NODE_PRIVATE(node);
494
495	if(hook == priv->lower)
496		priv->lower = NULL;
497	else if(hook == priv->upper)
498		priv->upper = NULL;
499	else
500		printf("%s: bogus hook %s\n", __func__, NG_HOOK_NAME(hook));
501
502	if (NG_NODE_NUMHOOKS(node) == 0) {
503		if (NG_NODE_IS_VALID(node))
504			ng_rmnode_self(node);
505	}
506
507	return (0);
508}
509
510/************************************************************/
511/*
512 * DATA
513 */
514/*
515 * Receive signal from USER.
516 *
517 * Repackage the data into one large buffer.
518 */
519static int
520ng_uni_rcvupper(hook_p hook, item_p item)
521{
522	node_p node = NG_HOOK_NODE(hook);
523	struct priv *priv = NG_NODE_PRIVATE(node);
524	struct mbuf *m;
525	struct uni_arg arg;
526	struct uni_msg *msg;
527	int error;
528
529	if (!priv->enabled) {
530		NG_FREE_ITEM(item);
531		return (ENOTCONN);
532	}
533
534	NGI_GET_M(item, m);
535	NG_FREE_ITEM(item);
536
537	if ((error = uni_msg_unpack_mbuf(m, &msg)) != 0) {
538		m_freem(m);
539		return (error);
540	}
541	m_freem(m);
542
543	if (uni_msg_len(msg) < sizeof(arg)) {
544		printf("%s: packet too short\n", __func__);
545		uni_msg_destroy(msg);
546		return (EINVAL);
547	}
548
549	bcopy(msg->b_rptr, &arg, sizeof(arg));
550	msg->b_rptr += sizeof(arg);
551
552	if (arg.sig >= UNIAPI_MAXSIG) {
553		printf("%s: bogus signal\n", __func__);
554		uni_msg_destroy(msg);
555		return (EINVAL);
556	}
557	uni_uni_input(priv->uni, arg.sig, arg.cookie, msg);
558	uni_work(priv->uni);
559
560	return (0);
561}
562
563/*
564 * Upper layer signal from UNI
565 */
566static void
567uni_uni_output(struct uni *uni, void *varg, enum uni_sig sig, u_int32_t cookie,
568    struct uni_msg *msg)
569{
570	node_p node = (node_p)varg;
571	struct priv *priv = NG_NODE_PRIVATE(node);
572	struct mbuf *m;
573	struct uni_arg arg;
574	int error;
575
576	if (priv->upper == NULL) {
577		if (msg != NULL)
578			uni_msg_destroy(msg);
579		return;
580	}
581	arg.sig = sig;
582	arg.cookie = cookie;
583
584	m = uni_msg_pack_mbuf(msg, &arg, sizeof(arg));
585	if (msg != NULL)
586		uni_msg_destroy(msg);
587	if (m == NULL)
588		return;
589
590	NG_SEND_DATA_ONLY(error, priv->upper, m);
591}
592
593static void
594dump_uni_msg(struct uni_msg *msg)
595{
596	u_int pos;
597
598	for (pos = 0; pos < uni_msg_len(msg); pos++) {
599		if (pos % 16 == 0)
600			printf("%06o ", pos);
601		if (pos % 16 == 8)
602			printf("  ");
603		printf(" %02x", msg->b_rptr[pos]);
604		if (pos % 16 == 15)
605			printf("\n");
606	}
607	if (pos % 16 != 0)
608		printf("\n");
609}
610
611/*
612 * Dump a SAAL signal in either direction
613 */
614static void
615dump_saal_signal(node_p node, enum saal_sig sig, struct uni_msg *msg, int to)
616{
617	struct priv *priv = NG_NODE_PRIVATE(node);
618
619	printf("signal %s SAAL: ", to ? "to" : "from");
620
621	switch (sig) {
622#define D(S) case S: printf("%s", #S); break
623
624	D(SAAL_ESTABLISH_request);
625	D(SAAL_ESTABLISH_indication);
626	D(SAAL_ESTABLISH_confirm);
627	D(SAAL_RELEASE_request);
628	D(SAAL_RELEASE_confirm);
629	D(SAAL_RELEASE_indication);
630	D(SAAL_DATA_request);
631	D(SAAL_DATA_indication);
632	D(SAAL_UDATA_request);
633	D(SAAL_UDATA_indication);
634
635#undef D
636	  default:
637		printf("sig=%d", sig); break;
638	}
639	if (msg != NULL) {
640		printf(" data=%zu\n", uni_msg_len(msg));
641		if (uni_get_debug(priv->uni, UNI_FAC_SAAL) > 1)
642			dump_uni_msg(msg);
643	} else
644		printf("\n");
645}
646
647/*
648 * Receive signal from SSCOP.
649 *
650 * If this is a data signal, repackage the data into one large buffer.
651 * UNI shouldn't be the bottleneck in a system and this greatly simplifies
652 * parsing in UNI.
653 */
654static int
655ng_uni_rcvlower(hook_p hook __unused, item_p item)
656{
657	node_p node = NG_HOOK_NODE(hook);
658	struct priv *priv = NG_NODE_PRIVATE(node);
659	struct mbuf *m;
660	struct sscfu_arg arg;
661	struct uni_msg *msg;
662	int error;
663
664	if (!priv->enabled) {
665		NG_FREE_ITEM(item);
666		return (ENOTCONN);
667	}
668
669	NGI_GET_M(item, m);
670	NG_FREE_ITEM(item);
671
672	if ((error = uni_msg_unpack_mbuf(m, &msg)) != 0) {
673		m_freem(m);
674		return (error);
675	}
676	m_freem(m);
677
678	if (uni_msg_len(msg) < sizeof(arg)) {
679		uni_msg_destroy(msg);
680		printf("%s: packet too short\n", __func__);
681		return (EINVAL);
682	}
683	bcopy(msg->b_rptr, &arg, sizeof(arg));
684	msg->b_rptr += sizeof(arg);
685
686	if (arg.sig > SAAL_UDATA_indication) {
687		uni_msg_destroy(msg);
688		printf("%s: bogus signal\n", __func__);
689		return (EINVAL);
690	}
691
692	if (uni_get_debug(priv->uni, UNI_FAC_SAAL) > 0)
693		dump_saal_signal(node, arg.sig, msg, 0);
694
695	uni_saal_input(priv->uni, arg.sig, msg);
696	uni_work(priv->uni);
697
698	return (0);
699}
700
701/*
702 * Send signal to sscop.
703 * Pack the message into an mbuf chain.
704 */
705static void
706uni_saal_output(struct uni *uni, void *varg, enum saal_sig sig, struct uni_msg *msg)
707{
708	node_p node = (node_p)varg;
709	struct priv *priv = NG_NODE_PRIVATE(node);
710	struct mbuf *m;
711	struct sscfu_arg arg;
712	int error;
713
714	if (uni_get_debug(priv->uni, UNI_FAC_SAAL) > 0)
715		dump_saal_signal(node, sig, msg, 1);
716
717	if (priv->lower == NULL) {
718		if (msg != NULL)
719			uni_msg_destroy(msg);
720		return;
721	}
722
723	arg.sig = sig;
724
725	m = uni_msg_pack_mbuf(msg, &arg, sizeof(arg));
726	if (msg != NULL)
727		uni_msg_destroy(msg);
728	if (m == NULL)
729		return;
730
731	NG_SEND_DATA_ONLY(error, priv->lower, m);
732}
733
734static void
735uni_verbose(struct uni *uni, void *varg, u_int fac, const char *fmt, ...)
736{
737	va_list ap;
738
739	static char *facnames[] = {
740#define UNI_DEBUG_DEFINE(D) [UNI_FAC_##D] = #D,
741		UNI_DEBUG_FACILITIES
742#undef UNI_DEBUG_DEFINE
743	};
744
745	printf("%s: ", facnames[fac]);
746
747	va_start(ap, fmt);
748	vprintf(fmt, ap);
749	va_end(ap);
750
751	printf("\n");
752}
753
754/************************************************************/
755/*
756 * Memory debugging
757 */
758struct unimem_debug {
759	const char	*file;
760	u_int		lno;
761	LIST_ENTRY(unimem_debug) link;
762	char		data[0];
763};
764LIST_HEAD(unimem_debug_list, unimem_debug);
765
766static struct unimem_debug_list nguni_freemem[UNIMEM_TYPES] = {
767    LIST_HEAD_INITIALIZER(nguni_freemem[0]),
768    LIST_HEAD_INITIALIZER(nguni_freemem[1]),
769    LIST_HEAD_INITIALIZER(nguni_freemem[2]),
770    LIST_HEAD_INITIALIZER(nguni_freemem[3]),
771    LIST_HEAD_INITIALIZER(nguni_freemem[4]),
772};
773static struct unimem_debug_list nguni_usedmem[UNIMEM_TYPES] = {
774    LIST_HEAD_INITIALIZER(nguni_usedmem[0]),
775    LIST_HEAD_INITIALIZER(nguni_usedmem[1]),
776    LIST_HEAD_INITIALIZER(nguni_usedmem[2]),
777    LIST_HEAD_INITIALIZER(nguni_usedmem[3]),
778    LIST_HEAD_INITIALIZER(nguni_usedmem[4]),
779};
780
781static struct mtx nguni_unilist_mtx;
782
783static const char *unimem_names[UNIMEM_TYPES] = {
784	"instance",
785	"all",
786	"signal",
787	"call",
788	"party"
789};
790
791static void
792uni_init(void)
793{
794	mtx_init(&nguni_unilist_mtx, "netgraph UNI structure lists", NULL,
795	    MTX_DEF);
796}
797
798static void
799uni_fini(void)
800{
801	u_int type;
802	struct unimem_debug *h;
803
804	for (type = 0; type < UNIMEM_TYPES; type++) {
805		while ((h = LIST_FIRST(&nguni_freemem[type])) != NULL) {
806			LIST_REMOVE(h, link);
807			free(h, M_UNI);
808		}
809
810		while ((h = LIST_FIRST(&nguni_usedmem[type])) != NULL) {
811			LIST_REMOVE(h, link);
812			printf("ng_uni: %s in use: %p (%s,%u)\n",
813			    unimem_names[type], (caddr_t)h->data,
814			    h->file, h->lno);
815			free(h, M_UNI);
816		}
817	}
818
819	mtx_destroy(&nguni_unilist_mtx);
820}
821
822/*
823 * Allocate a chunk of memory from a given type.
824 */
825void *
826ng_uni_malloc(enum unimem type, const char *file, u_int lno)
827{
828	struct unimem_debug *d;
829	size_t full;
830
831	/*
832	 * Try to allocate
833	 */
834	mtx_lock(&nguni_unilist_mtx);
835	if ((d = LIST_FIRST(&nguni_freemem[type])) != NULL)
836		LIST_REMOVE(d, link);
837	mtx_unlock(&nguni_unilist_mtx);
838
839	if (d == NULL) {
840		/*
841		 * allocate
842		 */
843		full = unimem_sizes[type] + offsetof(struct unimem_debug, data);
844		if ((d = malloc(full, M_UNI, M_NOWAIT | M_ZERO)) == NULL)
845			return (NULL);
846	} else {
847		bzero(d->data, unimem_sizes[type]);
848	}
849	d->file = file;
850	d->lno = lno;
851
852	mtx_lock(&nguni_unilist_mtx);
853	LIST_INSERT_HEAD(&nguni_usedmem[type], d, link);
854	mtx_unlock(&nguni_unilist_mtx);
855	return (d->data);
856}
857
858void
859ng_uni_free(enum unimem type, void *ptr, const char *file, u_int lno)
860{
861	struct unimem_debug *d, *h;
862
863	d = (struct unimem_debug *)
864	    ((char *)ptr - offsetof(struct unimem_debug, data));
865
866	mtx_lock(&nguni_unilist_mtx);
867
868	LIST_FOREACH(h, &nguni_usedmem[type], link)
869		if (d == h)
870			break;
871
872	if (h != NULL) {
873		LIST_REMOVE(d, link);
874		LIST_INSERT_HEAD(&nguni_freemem[type], d, link);
875	} else {
876		/*
877		 * Not on used list - try free list.
878		 */
879		LIST_FOREACH(h, &nguni_freemem[type], link)
880			if (d == h)
881				break;
882		if (h == NULL)
883			printf("ng_uni: %s,%u: %p(%s) was never allocated\n",
884			    file, lno, ptr, unimem_names[type]);
885		else
886			printf("ng_uni: %s,%u: %p(%s) was already destroyed "
887			    "in %s,%u\n",
888			    file, lno, ptr, unimem_names[type],
889			    h->file, h->lno);
890	}
891	mtx_unlock(&nguni_unilist_mtx);
892}
893/************************************************************/
894/*
895 * INITIALISATION
896 */
897
898/*
899 * Loading and unloading of node type
900 */
901static int
902ng_uni_mod_event(module_t mod, int event, void *data)
903{
904	int error = 0;
905
906	switch(event) {
907	  case MOD_LOAD:
908		uni_init();
909		break;
910
911	  case MOD_UNLOAD:
912		uni_fini();
913		break;
914
915	  default:
916		error = EOPNOTSUPP;
917		break;
918	}
919	return (error);
920}
921