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 * 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 * Author: Hartmut Brandt <harti@freebsd.org>
30 *
31 * Netgraph module for ITU-T Q.2120 UNI SSCF.
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/sbuf.h>
47#include <machine/stdarg.h>
48
49#include <netgraph/ng_message.h>
50#include <netgraph/netgraph.h>
51#include <netgraph/ng_parse.h>
52#include <netnatm/saal/sscopdef.h>
53#include <netnatm/saal/sscfudef.h>
54#include <netgraph/atm/ng_sscop.h>
55#include <netgraph/atm/ng_sscfu.h>
56#include <netgraph/atm/sscfu/ng_sscfu_cust.h>
57#include <netnatm/saal/sscfu.h>
58
59MALLOC_DEFINE(M_NG_SSCFU, "netgraph_sscfu", "netgraph uni sscf node");
60
61MODULE_DEPEND(ng_sscfu, ngatmbase, 1, 1, 1);
62
63/*
64 * Private data
65 */
66struct priv {
67	hook_p		upper;	/* SAAL interface */
68	hook_p		lower;	/* SSCOP interface */
69	struct sscfu	*sscf;	/* the instance */
70	int		enabled;
71};
72
73/*
74 * PARSING
75 */
76/*
77 * Parse PARAM type
78 */
79static const struct ng_parse_struct_field ng_sscop_param_type_info[] =
80    NG_SSCOP_PARAM_INFO;
81
82static const struct ng_parse_type ng_sscop_param_type = {
83	&ng_parse_struct_type,
84	ng_sscop_param_type_info
85};
86
87static const struct ng_parse_struct_field ng_sscfu_getdefparam_type_info[] =
88    NG_SSCFU_GETDEFPARAM_INFO;
89
90static const struct ng_parse_type ng_sscfu_getdefparam_type = {
91	&ng_parse_struct_type,
92	ng_sscfu_getdefparam_type_info
93};
94
95static const struct ng_cmdlist ng_sscfu_cmdlist[] = {
96	{
97	  NGM_SSCFU_COOKIE,
98	  NGM_SSCFU_GETDEFPARAM,
99	  "getdefparam",
100	  NULL,
101	  &ng_sscfu_getdefparam_type
102	},
103	{
104	  NGM_SSCFU_COOKIE,
105	  NGM_SSCFU_ENABLE,
106	  "enable",
107	  NULL,
108	  NULL
109	},
110	{
111	  NGM_SSCFU_COOKIE,
112	  NGM_SSCFU_DISABLE,
113	  "disable",
114	  NULL,
115	  NULL
116	},
117	{
118	  NGM_SSCFU_COOKIE,
119	  NGM_SSCFU_GETDEBUG,
120	  "getdebug",
121	  NULL,
122	  &ng_parse_hint32_type
123	},
124	{
125	  NGM_SSCFU_COOKIE,
126	  NGM_SSCFU_SETDEBUG,
127	  "setdebug",
128	  &ng_parse_hint32_type,
129	  NULL
130	},
131	{
132	  NGM_SSCFU_COOKIE,
133	  NGM_SSCFU_GETSTATE,
134	  "getstate",
135	  NULL,
136	  &ng_parse_uint32_type
137	},
138	{ 0 }
139};
140
141static ng_constructor_t ng_sscfu_constructor;
142static ng_shutdown_t	ng_sscfu_shutdown;
143static ng_rcvmsg_t	ng_sscfu_rcvmsg;
144static ng_newhook_t	ng_sscfu_newhook;
145static ng_disconnect_t	ng_sscfu_disconnect;
146static ng_rcvdata_t	ng_sscfu_rcvupper;
147static ng_rcvdata_t	ng_sscfu_rcvlower;
148
149static int ng_sscfu_mod_event(module_t, int, void *);
150
151static struct ng_type ng_sscfu_typestruct = {
152	.version =	NG_ABI_VERSION,
153	.name =		NG_SSCFU_NODE_TYPE,
154	.mod_event =	ng_sscfu_mod_event,
155	.constructor =	ng_sscfu_constructor,
156	.rcvmsg =	ng_sscfu_rcvmsg,
157	.shutdown =	ng_sscfu_shutdown,
158	.newhook =	ng_sscfu_newhook,
159	.rcvdata =	ng_sscfu_rcvupper,
160	.disconnect =	ng_sscfu_disconnect,
161	.cmdlist =	ng_sscfu_cmdlist,
162};
163NETGRAPH_INIT(sscfu, &ng_sscfu_typestruct);
164
165static void sscfu_send_upper(struct sscfu *, void *, enum saal_sig,
166	struct mbuf *);
167static void sscfu_send_lower(struct sscfu *, void *, enum sscop_aasig,
168	struct mbuf *, u_int);
169static void sscfu_window(struct sscfu *, void *, u_int);
170static void sscfu_verbose(struct sscfu *, void *, const char *, ...)
171	__printflike(3, 4);
172
173static const struct sscfu_funcs sscfu_funcs = {
174	sscfu_send_upper,
175	sscfu_send_lower,
176	sscfu_window,
177	sscfu_verbose
178};
179
180/************************************************************/
181/*
182 * CONTROL MESSAGES
183 */
184static int
185text_status(node_p node, struct priv *priv, char *arg, u_int len)
186{
187	struct sbuf sbuf;
188
189	sbuf_new(&sbuf, arg, len, 0);
190
191	if (priv->upper)
192		sbuf_printf(&sbuf, "upper hook: %s connected to %s:%s\n",
193		    NG_HOOK_NAME(priv->upper),
194		    NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->upper))),
195		    NG_HOOK_NAME(NG_HOOK_PEER(priv->upper)));
196	else
197		sbuf_printf(&sbuf, "upper hook: <not connected>\n");
198
199	if (priv->lower)
200		sbuf_printf(&sbuf, "lower hook: %s connected to %s:%s\n",
201		    NG_HOOK_NAME(priv->lower),
202		    NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->lower))),
203		    NG_HOOK_NAME(NG_HOOK_PEER(priv->lower)));
204	else
205		sbuf_printf(&sbuf, "lower hook: <not connected>\n");
206
207	sbuf_printf(&sbuf, "sscf state: %s\n",
208	    priv->enabled == 0 ? "<disabled>" :
209	    sscfu_statename(sscfu_getstate(priv->sscf)));
210
211	sbuf_finish(&sbuf);
212	return (sbuf_len(&sbuf));
213}
214
215static int
216ng_sscfu_rcvmsg(node_p node, item_p item, hook_p lasthook)
217{
218	struct priv *priv = NG_NODE_PRIVATE(node);
219	struct ng_mesg *resp = NULL;
220	struct ng_mesg *msg;
221	int error = 0;
222
223	NGI_GET_MSG(item, msg);
224
225	switch (msg->header.typecookie) {
226	  case NGM_GENERIC_COOKIE:
227		switch (msg->header.cmd) {
228		  case NGM_TEXT_STATUS:
229			NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
230			if (resp == NULL) {
231				error = ENOMEM;
232				break;
233			}
234			resp->header.arglen = text_status(node, priv,
235			    (char *)resp->data, resp->header.arglen) + 1;
236			break;
237
238		  default:
239			error = EINVAL;
240			break;
241		}
242		break;
243
244	  case NGM_SSCFU_COOKIE:
245		switch (msg->header.cmd) {
246		  case NGM_SSCFU_GETDEFPARAM:
247		    {
248			struct ng_sscfu_getdefparam *p;
249
250			if (msg->header.arglen != 0) {
251				error = EINVAL;
252				break;
253			}
254			NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
255			if (resp == NULL) {
256				error = ENOMEM;
257				break;
258			}
259			p = (struct ng_sscfu_getdefparam *)resp->data;
260			p->mask = sscfu_getdefparam(&p->param);
261			break;
262		    }
263
264		  case NGM_SSCFU_ENABLE:
265			if (msg->header.arglen != 0) {
266				error = EINVAL;
267				break;
268			}
269			if (priv->enabled) {
270				error = EISCONN;
271				break;
272			}
273			priv->enabled = 1;
274			break;
275
276		  case NGM_SSCFU_DISABLE:
277			if (msg->header.arglen != 0) {
278				error = EINVAL;
279				break;
280			}
281			if (!priv->enabled) {
282				error = ENOTCONN;
283				break;
284			}
285			priv->enabled = 0;
286			sscfu_reset(priv->sscf);
287			break;
288
289		  case NGM_SSCFU_GETSTATE:
290			if (msg->header.arglen != 0) {
291				error = EINVAL;
292				break;
293			}
294			NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT);
295			if(resp == NULL) {
296				error = ENOMEM;
297				break;
298			}
299			*(uint32_t *)resp->data =
300			    priv->enabled ? (sscfu_getstate(priv->sscf) + 1)
301			                  : 0;
302			break;
303
304		  case NGM_SSCFU_GETDEBUG:
305			if (msg->header.arglen != 0) {
306				error = EINVAL;
307				break;
308			}
309			NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT);
310			if(resp == NULL) {
311				error = ENOMEM;
312				break;
313			}
314			*(uint32_t *)resp->data = sscfu_getdebug(priv->sscf);
315			break;
316
317		  case NGM_SSCFU_SETDEBUG:
318			if (msg->header.arglen != sizeof(uint32_t)) {
319				error = EINVAL;
320				break;
321			}
322			sscfu_setdebug(priv->sscf, *(uint32_t *)msg->data);
323			break;
324
325		  default:
326			error = EINVAL;
327			break;
328		}
329		break;
330
331	  default:
332		error = EINVAL;
333		break;
334	}
335
336	NG_RESPOND_MSG(error, node, item, resp);
337	NG_FREE_MSG(msg);
338
339	return (error);
340}
341
342/************************************************************/
343/*
344 * HOOK MANAGEMENT
345 */
346static int
347ng_sscfu_newhook(node_p node, hook_p hook, const char *name)
348{
349	struct priv *priv = NG_NODE_PRIVATE(node);
350
351	if (strcmp(name, "upper") == 0)
352		priv->upper = hook;
353	else if (strcmp(name, "lower") == 0) {
354		priv->lower = hook;
355		NG_HOOK_SET_RCVDATA(hook, ng_sscfu_rcvlower);
356	} else
357		return (EINVAL);
358	return (0);
359}
360
361static int
362ng_sscfu_disconnect(hook_p hook)
363{
364	node_p node = NG_HOOK_NODE(hook);
365	struct priv *priv = NG_NODE_PRIVATE(node);
366
367	if (hook == priv->upper)
368		priv->upper = NULL;
369	else if (hook == priv->lower)
370		priv->lower = NULL;
371	else {
372		log(LOG_ERR, "bogus hook");
373		return (EINVAL);
374	}
375
376	if (NG_NODE_NUMHOOKS(node) == 0) {
377		if (NG_NODE_IS_VALID(node))
378			ng_rmnode_self(node);
379	} else {
380		/*
381		 * Because there are no timeouts reset the protocol
382		 * if the lower layer is disconnected.
383		 */
384		if (priv->lower == NULL &&
385		    priv->enabled &&
386		    sscfu_getstate(priv->sscf) != SSCFU_RELEASED)
387			sscfu_reset(priv->sscf);
388	}
389	return (0);
390}
391
392/************************************************************/
393/*
394 * DATA
395 */
396static int
397ng_sscfu_rcvupper(hook_p hook, item_p item)
398{
399	node_p node = NG_HOOK_NODE(hook);
400	struct priv *priv = NG_NODE_PRIVATE(node);
401	struct mbuf *m;
402	struct sscfu_arg a;
403
404	if (!priv->enabled || priv->lower == NULL) {
405		NG_FREE_ITEM(item);
406		return (0);
407	}
408
409	NGI_GET_M(item, m);
410	NG_FREE_ITEM(item);
411
412	if (!(m->m_flags & M_PKTHDR)) {
413		printf("no pkthdr\n");
414		m_freem(m);
415		return (EINVAL);
416	}
417	if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
418		return (ENOMEM);
419	bcopy((caddr_t)mtod(m, struct sscfu_arg *), &a, sizeof(a));
420	m_adj(m, sizeof(a));
421
422	return (sscfu_saalsig(priv->sscf, a.sig, m));
423}
424
425static void
426sscfu_send_upper(struct sscfu *sscf, void *p, enum saal_sig sig, struct mbuf *m)
427{
428	node_p node = (node_p)p;
429	struct priv *priv = NG_NODE_PRIVATE(node);
430	int error;
431	struct sscfu_arg *a;
432
433	if (priv->upper == NULL) {
434		if (m != NULL)
435			m_freem(m);
436		return;
437	}
438	if (m == NULL) {
439		MGETHDR(m, M_NOWAIT, MT_DATA);
440		if (m == NULL)
441			return;
442		m->m_len = sizeof(struct sscfu_arg);
443		m->m_pkthdr.len = m->m_len;
444	} else {
445		M_PREPEND(m, sizeof(struct sscfu_arg), M_NOWAIT);
446		if (m == NULL)
447			return;
448	}
449	a = mtod(m, struct sscfu_arg *);
450	a->sig = sig;
451
452	NG_SEND_DATA_ONLY(error, priv->upper, m);
453}
454
455static int
456ng_sscfu_rcvlower(hook_p hook, item_p item)
457{
458	node_p node = NG_HOOK_NODE(hook);
459	struct priv *priv = NG_NODE_PRIVATE(node);
460	struct mbuf *m;
461	struct sscop_arg a;
462
463	if (!priv->enabled || priv->upper == NULL) {
464		NG_FREE_ITEM(item);
465		return (0);
466	}
467
468	NGI_GET_M(item, m);
469	NG_FREE_ITEM(item);
470
471	if (!(m->m_flags & M_PKTHDR)) {
472		printf("no pkthdr\n");
473		m_freem(m);
474		return (EINVAL);
475	}
476
477	/*
478	 * Strip of the SSCOP header.
479	 */
480	if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
481		return (ENOMEM);
482	bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a));
483	m_adj(m, sizeof(a));
484
485	sscfu_input(priv->sscf, a.sig, m, a.arg);
486
487	return (0);
488}
489
490static void
491sscfu_send_lower(struct sscfu *sscf, void *p, enum sscop_aasig sig,
492    struct mbuf *m, u_int arg)
493{
494	node_p node = (node_p)p;
495	struct priv *priv = NG_NODE_PRIVATE(node);
496	int error;
497	struct sscop_arg *a;
498
499	if (priv->lower == NULL) {
500		if (m != NULL)
501			m_freem(m);
502		return;
503	}
504	if (m == NULL) {
505		MGETHDR(m, M_NOWAIT, MT_DATA);
506		if (m == NULL)
507			return;
508		m->m_len = sizeof(struct sscop_arg);
509		m->m_pkthdr.len = m->m_len;
510	} else {
511		M_PREPEND(m, sizeof(struct sscop_arg), M_NOWAIT);
512		if (m == NULL)
513			return;
514	}
515	a = mtod(m, struct sscop_arg *);
516	a->sig = sig;
517	a->arg = arg;
518
519	NG_SEND_DATA_ONLY(error, priv->lower, m);
520}
521
522/*
523 * Window is handled by ng_sscop so make this a NOP.
524 */
525static void
526sscfu_window(struct sscfu *sscfu, void *arg, u_int w)
527{
528}
529
530/************************************************************/
531/*
532 * NODE MANAGEMENT
533 */
534static int
535ng_sscfu_constructor(node_p node)
536{
537	struct priv *priv;
538
539	priv = malloc(sizeof(*priv), M_NG_SSCFU, M_WAITOK | M_ZERO);
540
541	if ((priv->sscf = sscfu_create(node, &sscfu_funcs)) == NULL) {
542		free(priv, M_NG_SSCFU);
543		return (ENOMEM);
544	}
545
546	NG_NODE_SET_PRIVATE(node, priv);
547
548	return (0);
549}
550
551static int
552ng_sscfu_shutdown(node_p node)
553{
554	struct priv *priv = NG_NODE_PRIVATE(node);
555
556	sscfu_destroy(priv->sscf);
557
558	free(priv, M_NG_SSCFU);
559	NG_NODE_SET_PRIVATE(node, NULL);
560
561	NG_NODE_UNREF(node);
562
563	return (0);
564}
565
566static void
567sscfu_verbose(struct sscfu *sscfu, void *arg, const char *fmt, ...)
568{
569	va_list ap;
570
571	va_start(ap, fmt);
572	printf("sscfu(%p): ", sscfu);
573	vprintf(fmt, ap);
574	va_end(ap);
575	printf("\n");
576}
577
578/************************************************************/
579/*
580 * INITIALISATION
581 */
582/*
583 * Loading and unloading of node type
584 */
585static int
586ng_sscfu_mod_event(module_t mod, int event, void *data)
587{
588	int error = 0;
589
590	switch (event) {
591	  case MOD_LOAD:
592		break;
593
594	  case MOD_UNLOAD:
595		break;
596
597	  default:
598		error = EOPNOTSUPP;
599		break;
600	}
601	return (error);
602}
603