1 /*-
2  * Copyright (c) 2001-2003
3  *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *	All rights reserved.
5  *
6  * Author: Harti Brandt <harti@freebsd.org>
7  *
8  * Redistribution of this software and documentation and use in source and
9  * binary forms, with or without modification, are permitted provided that
10  * the following conditions are met:
11  *
12  * 1. Redistributions of source code or documentation must retain the above
13  *    copyright notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
19  * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
22  * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
25  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * Netgraph module for ITU-T Q.2110 SSCOP.
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD: stable/12/sys/netgraph/atm/sscop/ng_sscop.c 372965 2023-03-06 21:23:38Z brooks $");
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/mbuf.h>
41 #include <sys/errno.h>
42 #include <sys/syslog.h>
43 #include <sys/socket.h>
44 #include <sys/socketvar.h>
45 #include <sys/callout.h>
46 #include <sys/sbuf.h>
47 #include <sys/stdint.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/saal/sscopdef.h>
54 #include <netgraph/atm/ng_sscop.h>
55 #include <netgraph/atm/sscop/ng_sscop_cust.h>
56 #include <netnatm/saal/sscop.h>
57 
58 #define DDD printf("%s: %d\n", __func__, __LINE__)
59 
60 #ifdef SSCOP_DEBUG
61 #define VERBOSE(P,M,F)							\
62     do {								\
63 	if (sscop_getdebug((P)->sscop) & (M))				\
64 		sscop_verbose F ;					\
65     } while(0)
66 #else
67 #define VERBOSE(P,M,F)
68 #endif
69 
70 MALLOC_DEFINE(M_NG_SSCOP, "netgraph_sscop", "netgraph sscop node");
71 
72 MODULE_DEPEND(ng_sscop, ngatmbase, 1, 1, 1);
73 
74 struct stats {
75 	uint64_t	in_packets;
76 	uint64_t	out_packets;
77 	uint64_t	aa_signals;
78 	uint64_t	errors;
79 	uint64_t	data_delivered;
80 	uint64_t	aa_dropped;
81 	uint64_t	maa_dropped;
82 	uint64_t	maa_signals;
83 	uint64_t	in_dropped;
84 	uint64_t	out_dropped;
85 };
86 
87 /*
88  * Private data
89  */
90 struct priv {
91 	hook_p		upper;		/* SAAL interface */
92 	hook_p		lower;		/* AAL5 interface */
93 	hook_p		manage;		/* management interface */
94 
95 	struct sscop	*sscop;		/* sscop state */
96 	int		enabled;	/* whether the protocol is enabled */
97 	int		flow;		/* flow control states */
98 	struct stats	stats;		/* sadistics */
99 };
100 
101 /*
102  * Parse PARAM type
103  */
104 static const struct ng_parse_struct_field ng_sscop_param_type_info[] =
105     NG_SSCOP_PARAM_INFO;
106 
107 static const struct ng_parse_type ng_sscop_param_type = {
108 	&ng_parse_struct_type,
109 	ng_sscop_param_type_info
110 };
111 
112 /*
113  * Parse a SET PARAM type.
114  */
115 static const struct ng_parse_struct_field ng_sscop_setparam_type_info[] =
116     NG_SSCOP_SETPARAM_INFO;
117 
118 static const struct ng_parse_type ng_sscop_setparam_type = {
119 	&ng_parse_struct_type,
120 	ng_sscop_setparam_type_info,
121 };
122 
123 /*
124  * Parse a SET PARAM response
125  */
126 static const struct ng_parse_struct_field ng_sscop_setparam_resp_type_info[] =
127     NG_SSCOP_SETPARAM_RESP_INFO;
128 
129 static const struct ng_parse_type ng_sscop_setparam_resp_type = {
130 	&ng_parse_struct_type,
131 	ng_sscop_setparam_resp_type_info,
132 };
133 
134 static const struct ng_cmdlist ng_sscop_cmdlist[] = {
135 	{
136 	  NGM_SSCOP_COOKIE,
137 	  NGM_SSCOP_GETPARAM,
138 	  "getparam",
139 	  NULL,
140 	  &ng_sscop_param_type
141 	},
142 	{
143 	  NGM_SSCOP_COOKIE,
144 	  NGM_SSCOP_SETPARAM,
145 	  "setparam",
146 	  &ng_sscop_setparam_type,
147 	  &ng_sscop_setparam_resp_type
148 	},
149 	{
150 	  NGM_SSCOP_COOKIE,
151 	  NGM_SSCOP_ENABLE,
152 	  "enable",
153 	  NULL,
154 	  NULL
155 	},
156 	{
157 	  NGM_SSCOP_COOKIE,
158 	  NGM_SSCOP_DISABLE,
159 	  "disable",
160 	  NULL,
161 	  NULL
162 	},
163 	{
164 	  NGM_SSCOP_COOKIE,
165 	  NGM_SSCOP_GETDEBUG,
166 	  "getdebug",
167 	  NULL,
168 	  &ng_parse_hint32_type
169 	},
170 	{
171 	  NGM_SSCOP_COOKIE,
172 	  NGM_SSCOP_SETDEBUG,
173 	  "setdebug",
174 	  &ng_parse_hint32_type,
175 	  NULL
176 	},
177 	{
178 	  NGM_SSCOP_COOKIE,
179 	  NGM_SSCOP_GETSTATE,
180 	  "getstate",
181 	  NULL,
182 	  &ng_parse_uint32_type
183 	},
184 	{ 0 }
185 };
186 
187 static ng_constructor_t ng_sscop_constructor;
188 static ng_shutdown_t	ng_sscop_shutdown;
189 static ng_rcvmsg_t	ng_sscop_rcvmsg;
190 static ng_newhook_t	ng_sscop_newhook;
191 static ng_disconnect_t	ng_sscop_disconnect;
192 static ng_rcvdata_t	ng_sscop_rcvlower;
193 static ng_rcvdata_t	ng_sscop_rcvupper;
194 static ng_rcvdata_t	ng_sscop_rcvmanage;
195 
196 static int ng_sscop_mod_event(module_t, int, void *);
197 
198 static struct ng_type ng_sscop_typestruct = {
199 	.version =	NG_ABI_VERSION,
200 	.name =		NG_SSCOP_NODE_TYPE,
201 	.mod_event =	ng_sscop_mod_event,
202 	.constructor =	ng_sscop_constructor,
203 	.rcvmsg =	ng_sscop_rcvmsg,
204 	.shutdown =	ng_sscop_shutdown,
205 	.newhook =	ng_sscop_newhook,
206 	.rcvdata =	ng_sscop_rcvlower,
207 	.disconnect =	ng_sscop_disconnect,
208 	.cmdlist =	ng_sscop_cmdlist,
209 };
210 NETGRAPH_INIT(sscop, &ng_sscop_typestruct);
211 
212 static void sscop_send_manage(struct sscop *, void *, enum sscop_maasig,
213 	struct SSCOP_MBUF_T *, u_int, u_int);
214 static void sscop_send_upper(struct sscop *, void *, enum sscop_aasig,
215 	struct SSCOP_MBUF_T *, u_int);
216 static void sscop_send_lower(struct sscop *, void *,
217 	struct SSCOP_MBUF_T *);
218 static void sscop_verbose(struct sscop *, void *, const char *, ...)
219 	__printflike(3, 4);
220 
221 static const struct sscop_funcs sscop_funcs = {
222 	sscop_send_manage,
223 	sscop_send_upper,
224 	sscop_send_lower,
225 	sscop_verbose
226 };
227 
228 static void
sscop_verbose(struct sscop * sscop,void * arg,const char * fmt,...)229 sscop_verbose(struct sscop *sscop, void *arg, const char *fmt, ...)
230 {
231 	va_list ap;
232 
233 	va_start(ap, fmt);
234 	printf("sscop(%p): ", sscop);
235 	vprintf(fmt, ap);
236 	va_end(ap);
237 	printf("\n");
238 }
239 
240 /************************************************************/
241 /*
242  * NODE MANAGEMENT
243  */
244 static int
ng_sscop_constructor(node_p node)245 ng_sscop_constructor(node_p node)
246 {
247 	struct priv *p;
248 
249 	gone_in(14, "ng_sscop: netgraph ATM modules");
250 
251 	p = malloc(sizeof(*p), M_NG_SSCOP, M_WAITOK | M_ZERO);
252 
253 	if ((p->sscop = sscop_create(node, &sscop_funcs)) == NULL) {
254 		free(p, M_NG_SSCOP);
255 		return (ENOMEM);
256 	}
257 	NG_NODE_SET_PRIVATE(node, p);
258 
259 	/* All data message received by the node are expected to change the
260 	 * node's state. Therefor we must ensure, that we have a writer lock. */
261 	NG_NODE_FORCE_WRITER(node);
262 
263 	return (0);
264 }
265 static int
ng_sscop_shutdown(node_p node)266 ng_sscop_shutdown(node_p node)
267 {
268 	struct priv *priv = NG_NODE_PRIVATE(node);
269 
270 	sscop_destroy(priv->sscop);
271 
272 	free(priv, M_NG_SSCOP);
273 	NG_NODE_SET_PRIVATE(node, NULL);
274 
275 	NG_NODE_UNREF(node);
276 
277 	return (0);
278 }
279 
280 /************************************************************/
281 /*
282  * CONTROL MESSAGES
283  */
284 /*
285  * Flow control message from upper layer.
286  * This is very experimental:
287  * If we get a message from the upper layer, that somebody has passed its
288  * high water mark, we stop updating the receive window.
289  * If we get a low watermark passed, then we raise the window up
290  * to max - current.
291  * If we get a queue status and it indicates a current below the
292  * high watermark, we unstop window updates (if they are stopped) and
293  * raise the window to highwater - current.
294  */
295 static int
flow_upper(node_p node,struct ng_mesg * msg)296 flow_upper(node_p node, struct ng_mesg *msg)
297 {
298 	struct ngm_queue_state *q;
299 	struct priv *priv = NG_NODE_PRIVATE(node);
300 	u_int window, space;
301 
302 	if (msg->header.arglen != sizeof(struct ngm_queue_state))
303 		return (EINVAL);
304 	q = (struct ngm_queue_state *)msg->data;
305 
306 	switch (msg->header.cmd) {
307 
308 	  case NGM_HIGH_WATER_PASSED:
309 		if (priv->flow) {
310 			VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
311 			    "flow control stopped"));
312 			priv->flow = 0;
313 		}
314 		break;
315 
316 	  case NGM_LOW_WATER_PASSED:
317 		window = sscop_window(priv->sscop, 0);
318 		space = q->max_queuelen_packets - q->current;
319 		if (space > window) {
320 			VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
321 			    "flow control opened window by %u messages",
322 			    space - window));
323 			(void)sscop_window(priv->sscop, space - window);
324 		}
325 		priv->flow = 1;
326 		break;
327 
328 	  case NGM_SYNC_QUEUE_STATE:
329 		if (q->high_watermark <= q->current)
330 			break;
331 		window = sscop_window(priv->sscop, 0);
332 		if (priv->flow)
333 			space = q->max_queuelen_packets - q->current;
334 		else
335 			space = q->high_watermark - q->current;
336 		if (space > window) {
337 			VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
338 			    "flow control opened window by %u messages",
339 			    space - window));
340 			(void)sscop_window(priv->sscop, space - window);
341 		}
342 		priv->flow = 1;
343 		break;
344 
345 	  default:
346 		return (EINVAL);
347 	}
348 	return (0);
349 }
350 
351 static int
flow_lower(node_p node,struct ng_mesg * msg)352 flow_lower(node_p node, struct ng_mesg *msg)
353 {
354 	struct priv *priv = NG_NODE_PRIVATE(node);
355 
356 	if (msg->header.arglen != sizeof(struct ngm_queue_state))
357 		return (EINVAL);
358 
359 	switch (msg->header.cmd) {
360 
361 	  case NGM_HIGH_WATER_PASSED:
362 		sscop_setbusy(priv->sscop, 1);
363 		break;
364 
365 	  case NGM_LOW_WATER_PASSED:
366 		sscop_setbusy(priv->sscop, 1);
367 		break;
368 
369 	  default:
370 		return (EINVAL);
371 	}
372 	return (0);
373 }
374 
375 /*
376  * Produce a readable status description
377  */
378 static int
text_status(node_p node,struct priv * priv,char * arg,u_int len)379 text_status(node_p node, struct priv *priv, char *arg, u_int len)
380 {
381 	struct sbuf sbuf;
382 
383 	sbuf_new(&sbuf, arg, len, 0);
384 
385 	if (priv->upper)
386 		sbuf_printf(&sbuf, "upper hook: %s connected to %s:%s\n",
387 		    NG_HOOK_NAME(priv->upper),
388 		    NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->upper))),
389 		    NG_HOOK_NAME(NG_HOOK_PEER(priv->upper)));
390 	else
391 		sbuf_printf(&sbuf, "upper hook: <not connected>\n");
392 
393 	if (priv->lower)
394 		sbuf_printf(&sbuf, "lower hook: %s connected to %s:%s\n",
395 		    NG_HOOK_NAME(priv->lower),
396 		    NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->lower))),
397 		    NG_HOOK_NAME(NG_HOOK_PEER(priv->lower)));
398 	else
399 		sbuf_printf(&sbuf, "lower hook: <not connected>\n");
400 
401 	if (priv->manage)
402 		sbuf_printf(&sbuf, "manage hook: %s connected to %s:%s\n",
403 		    NG_HOOK_NAME(priv->manage),
404 		    NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->manage))),
405 		    NG_HOOK_NAME(NG_HOOK_PEER(priv->manage)));
406 	else
407 		sbuf_printf(&sbuf, "manage hook: <not connected>\n");
408 
409 	sbuf_printf(&sbuf, "sscop state: %s\n",
410 	    !priv->enabled ? "<disabled>" :
411 	    sscop_statename(sscop_getstate(priv->sscop)));
412 
413 	sbuf_printf(&sbuf, "input packets:  %ju\n",
414 	    (uintmax_t)priv->stats.in_packets);
415 	sbuf_printf(&sbuf, "input dropped:  %ju\n",
416 	    (uintmax_t)priv->stats.in_dropped);
417 	sbuf_printf(&sbuf, "output packets: %ju\n",
418 	    (uintmax_t)priv->stats.out_packets);
419 	sbuf_printf(&sbuf, "output dropped: %ju\n",
420 	    (uintmax_t)priv->stats.out_dropped);
421 	sbuf_printf(&sbuf, "aa signals:     %ju\n",
422 	    (uintmax_t)priv->stats.aa_signals);
423 	sbuf_printf(&sbuf, "aa dropped:     %ju\n",
424 	    (uintmax_t)priv->stats.aa_dropped);
425 	sbuf_printf(&sbuf, "maa signals:    %ju\n",
426 	    (uintmax_t)priv->stats.maa_signals);
427 	sbuf_printf(&sbuf, "maa dropped:    %ju\n",
428 	    (uintmax_t)priv->stats.maa_dropped);
429 	sbuf_printf(&sbuf, "errors:         %ju\n",
430 	    (uintmax_t)priv->stats.errors);
431 	sbuf_printf(&sbuf, "data delivered: %ju\n",
432 	    (uintmax_t)priv->stats.data_delivered);
433 	sbuf_printf(&sbuf, "window:         %u\n",
434 	    sscop_window(priv->sscop, 0));
435 
436 	sbuf_finish(&sbuf);
437 	return (sbuf_len(&sbuf));
438 }
439 
440 
441 /*
442  * Control message received.
443  */
444 static int
ng_sscop_rcvmsg(node_p node,item_p item,hook_p lasthook)445 ng_sscop_rcvmsg(node_p node, item_p item, hook_p lasthook)
446 {
447 	struct priv *priv = NG_NODE_PRIVATE(node);
448 	struct ng_mesg *resp = NULL;
449 	struct ng_mesg *msg;
450 	int error = 0;
451 
452 	NGI_GET_MSG(item, msg);
453 
454 	switch (msg->header.typecookie) {
455 
456 	  case NGM_GENERIC_COOKIE:
457 		switch (msg->header.cmd) {
458 
459 		  case NGM_TEXT_STATUS:
460 			NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
461 			if (resp == NULL) {
462 				error = ENOMEM;
463 				break;
464 			}
465 
466 			resp->header.arglen = text_status(node, priv,
467 			    (char *)resp->data, resp->header.arglen) + 1;
468 			break;
469 
470 		  default:
471 			error = EINVAL;
472 			break;
473 		}
474 		break;
475 
476 	  case NGM_FLOW_COOKIE:
477 		if (priv->enabled && lasthook != NULL) {
478 			if (lasthook == priv->upper)
479 				error = flow_upper(node, msg);
480 			else if (lasthook == priv->lower)
481 				error = flow_lower(node, msg);
482 		}
483 		break;
484 
485 	  case NGM_SSCOP_COOKIE:
486 		switch (msg->header.cmd) {
487 
488 		  case NGM_SSCOP_GETPARAM:
489 		    {
490 			struct sscop_param *p;
491 
492 			NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
493 			if (resp == NULL) {
494 				error = ENOMEM;
495 				break;
496 			}
497 			p = (struct sscop_param *)resp->data;
498 			sscop_getparam(priv->sscop, p);
499 			break;
500 		    }
501 
502 		  case NGM_SSCOP_SETPARAM:
503 		    {
504 			struct ng_sscop_setparam *arg;
505 			struct ng_sscop_setparam_resp *p;
506 
507 			if (msg->header.arglen != sizeof(*arg)) {
508 				error = EINVAL;
509 				break;
510 			}
511 			if (priv->enabled) {
512 				error = EISCONN;
513 				break;
514 			}
515 			arg = (struct ng_sscop_setparam *)msg->data;
516 			NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
517 			if (resp == NULL) {
518 				error = ENOMEM;
519 				break;
520 			}
521 			p = (struct ng_sscop_setparam_resp *)resp->data;
522 			p->mask = arg->mask;
523 			p->error = sscop_setparam(priv->sscop,
524 			    &arg->param, &p->mask);
525 			break;
526 		    }
527 
528 		  case NGM_SSCOP_ENABLE:
529 			if (msg->header.arglen != 0) {
530 				error = EINVAL;
531 				break;
532 			}
533 			if (priv->enabled) {
534 				error = EBUSY;
535 				break;
536 			}
537 			priv->enabled = 1;
538 			priv->flow = 1;
539 			memset(&priv->stats, 0, sizeof(priv->stats));
540 			break;
541 
542 		  case NGM_SSCOP_DISABLE:
543 			if (msg->header.arglen != 0) {
544 				error = EINVAL;
545 				break;
546 			}
547 			if (!priv->enabled) {
548 				error = ENOTCONN;
549 				break;
550 			}
551 			priv->enabled = 0;
552 			sscop_reset(priv->sscop);
553 			break;
554 
555 		  case NGM_SSCOP_GETDEBUG:
556 			if (msg->header.arglen != 0) {
557 				error = EINVAL;
558 				break;
559 			}
560 			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
561 			if(resp == NULL) {
562 				error = ENOMEM;
563 				break;
564 			}
565 			*(u_int32_t *)resp->data = sscop_getdebug(priv->sscop);
566 			break;
567 
568 		  case NGM_SSCOP_SETDEBUG:
569 			if (msg->header.arglen != sizeof(u_int32_t)) {
570 				error = EINVAL;
571 				break;
572 			}
573 			sscop_setdebug(priv->sscop, *(u_int32_t *)msg->data);
574 			break;
575 
576 		  case NGM_SSCOP_GETSTATE:
577 			if (msg->header.arglen != 0) {
578 				error = EINVAL;
579 				break;
580 			}
581 			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
582 			if(resp == NULL) {
583 				error = ENOMEM;
584 				break;
585 			}
586 			*(u_int32_t *)resp->data =
587 			    priv->enabled ? (sscop_getstate(priv->sscop) + 1)
588 			                  : 0;
589 			break;
590 
591 		  default:
592 			error = EINVAL;
593 			break;
594 		}
595 		break;
596 
597 	  default:
598 		error = EINVAL;
599 		break;
600 	}
601 
602 	NG_RESPOND_MSG(error, node, item, resp);
603 	NG_FREE_MSG(msg);
604 
605 	return (error);
606 }
607 
608 /************************************************************/
609 /*
610  * HOOK MANAGEMENT
611  */
612 static int
ng_sscop_newhook(node_p node,hook_p hook,const char * name)613 ng_sscop_newhook(node_p node, hook_p hook, const char *name)
614 {
615 	struct priv *priv = NG_NODE_PRIVATE(node);
616 
617 	if(strcmp(name, "upper") == 0) {
618 		priv->upper = hook;
619 		NG_HOOK_SET_RCVDATA(hook, ng_sscop_rcvupper);
620 	} else if(strcmp(name, "lower") == 0) {
621 		priv->lower = hook;
622 	} else if(strcmp(name, "manage") == 0) {
623 		priv->manage = hook;
624 		NG_HOOK_SET_RCVDATA(hook, ng_sscop_rcvmanage);
625 	} else
626 		return EINVAL;
627 
628 	return 0;
629 }
630 static int
ng_sscop_disconnect(hook_p hook)631 ng_sscop_disconnect(hook_p hook)
632 {
633 	node_p node = NG_HOOK_NODE(hook);
634 	struct priv *priv = NG_NODE_PRIVATE(node);
635 
636 	if(hook == priv->upper)
637 		priv->upper = NULL;
638 	else if(hook == priv->lower)
639 		priv->lower = NULL;
640 	else if(hook == priv->manage)
641 		priv->manage = NULL;
642 
643 	if(NG_NODE_NUMHOOKS(node) == 0) {
644 		if(NG_NODE_IS_VALID(node))
645 			ng_rmnode_self(node);
646 	} else {
647 		/*
648 		 * Imply a release request, if the upper layer is
649 		 * disconnected.
650 		 */
651 		if(priv->upper == NULL && priv->lower != NULL &&
652 		   priv->enabled &&
653 		   sscop_getstate(priv->sscop) != SSCOP_IDLE) {
654 			sscop_aasig(priv->sscop, SSCOP_RELEASE_request,
655 			    NULL, 0);
656 		}
657 	}
658 	return 0;
659 }
660 
661 /************************************************************/
662 /*
663  * DATA
664  */
665 static int
ng_sscop_rcvlower(hook_p hook,item_p item)666 ng_sscop_rcvlower(hook_p hook, item_p item)
667 {
668 	struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
669 	struct mbuf *m;
670 
671 	if (!priv->enabled) {
672 		NG_FREE_ITEM(item);
673 		return EINVAL;
674 	}
675 
676 	/*
677 	 * If we are disconnected at the upper layer and in the IDLE
678 	 * state, drop any incoming packet.
679 	 */
680 	if (priv->upper != NULL || sscop_getstate(priv->sscop) != SSCOP_IDLE) {
681 		NGI_GET_M(item, m);
682 		priv->stats.in_packets++;
683 		sscop_input(priv->sscop, m);
684 	} else {
685 		priv->stats.in_dropped++;
686 	}
687 	NG_FREE_ITEM(item);
688 
689 	return (0);
690 }
691 
692 static void
sscop_send_lower(struct sscop * sscop,void * p,struct mbuf * m)693 sscop_send_lower(struct sscop *sscop, void *p, struct mbuf *m)
694 {
695 	node_p node = (node_p)p;
696 	struct priv *priv = NG_NODE_PRIVATE(node);
697 	int error;
698 
699 	if (priv->lower == NULL) {
700 		m_freem(m);
701 		priv->stats.out_dropped++;
702 		return;
703 	}
704 
705 	priv->stats.out_packets++;
706 	NG_SEND_DATA_ONLY(error, priv->lower, m);
707 }
708 
709 static int
ng_sscop_rcvupper(hook_p hook,item_p item)710 ng_sscop_rcvupper(hook_p hook, item_p item)
711 {
712 	struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
713 	struct sscop_arg a;
714 	struct mbuf *m;
715 
716 	if (!priv->enabled) {
717 		NG_FREE_ITEM(item);
718 		return (EINVAL);
719 	}
720 
721 	/*
722 	 * If the lower layer is not connected allow to proceed.
723 	 * The lower layer sending function will drop outgoing frames,
724 	 * and the sscop will timeout any establish requests.
725 	 */
726 	NGI_GET_M(item, m);
727 	NG_FREE_ITEM(item);
728 
729 	if (!(m->m_flags & M_PKTHDR)) {
730 		printf("no pkthdr\n");
731 		m_freem(m);
732 		return (EINVAL);
733 	}
734 	if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
735 		return (ENOBUFS);
736 	bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a));
737 	m_adj(m, sizeof(a));
738 
739 	return (sscop_aasig(priv->sscop, a.sig, m, a.arg));
740 }
741 
742 static void
sscop_send_upper(struct sscop * sscop,void * p,enum sscop_aasig sig,struct SSCOP_MBUF_T * m,u_int arg)743 sscop_send_upper(struct sscop *sscop, void *p, enum sscop_aasig sig,
744     struct SSCOP_MBUF_T *m, u_int arg)
745 {
746 	node_p node = (node_p)p;
747 	struct priv *priv = NG_NODE_PRIVATE(node);
748 	int error;
749 	struct sscop_arg *a;
750 
751 	if (sig == SSCOP_DATA_indication && priv->flow)
752 		sscop_window(priv->sscop, 1);
753 
754 	if (priv->upper == NULL) {
755 		if (m != NULL)
756 			m_freem(m);
757 		priv->stats.aa_dropped++;
758 		return;
759 	}
760 
761 	priv->stats.aa_signals++;
762 	if (sig == SSCOP_DATA_indication)
763 		priv->stats.data_delivered++;
764 
765 	if (m == NULL) {
766 		MGETHDR(m, M_NOWAIT, MT_DATA);
767 		if (m == NULL)
768 			return;
769 		m->m_len = sizeof(struct sscop_arg);
770 		m->m_pkthdr.len = m->m_len;
771 	} else {
772 		M_PREPEND(m, sizeof(struct sscop_arg), M_NOWAIT);
773 		if (m == NULL)
774 			return;
775 	}
776 	a = mtod(m, struct sscop_arg *);
777 	a->sig = sig;
778 	a->arg = arg;
779 
780 	NG_SEND_DATA_ONLY(error, priv->upper, m);
781 }
782 
783 static int
ng_sscop_rcvmanage(hook_p hook,item_p item)784 ng_sscop_rcvmanage(hook_p hook, item_p item)
785 {
786 	struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
787 	struct sscop_marg a;
788 	struct mbuf *m;
789 
790 	if (!priv->enabled) {
791 		NG_FREE_ITEM(item);
792 		return (EINVAL);
793 	}
794 
795 	NGI_GET_M(item, m);
796 	NG_FREE_ITEM(item);
797 
798 	if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
799 		return (ENOBUFS);
800 	bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a));
801 	m_adj(m, sizeof(a));
802 
803 	return (sscop_maasig(priv->sscop, a.sig, m));
804 }
805 
806 static void
sscop_send_manage(struct sscop * sscop,void * p,enum sscop_maasig sig,struct SSCOP_MBUF_T * m,u_int err,u_int cnt)807 sscop_send_manage(struct sscop *sscop, void *p, enum sscop_maasig sig,
808     struct SSCOP_MBUF_T *m, u_int err, u_int cnt)
809 {
810 	node_p node = (node_p)p;
811 	struct priv *priv = NG_NODE_PRIVATE(node);
812 	int error;
813 	struct sscop_merr *e;
814 	struct sscop_marg *a;
815 
816 	if (priv->manage == NULL) {
817 		if (m != NULL)
818 			m_freem(m);
819 		priv->stats.maa_dropped++;
820 		return;
821 	}
822 
823 	if (sig == SSCOP_MERROR_indication) {
824 		MGETHDR(m, M_NOWAIT, MT_DATA);
825 		if (m == NULL)
826 			return;
827 		m->m_len = sizeof(*e);
828 		m->m_pkthdr.len = m->m_len;
829 		e = mtod(m, struct sscop_merr *);
830 		e->sig = sig;
831 		e->err = err;
832 		e->cnt = cnt;
833 		priv->stats.errors++;
834 	} else if (m == NULL) {
835 		MGETHDR(m, M_NOWAIT, MT_DATA);
836 		if (m == NULL)
837 			return;
838 		m->m_len = sizeof(*a);
839 		m->m_pkthdr.len = m->m_len;
840 		a = mtod(m, struct sscop_marg *);
841 		a->sig = sig;
842 		priv->stats.maa_signals++;
843 	} else {
844 		M_PREPEND(m, sizeof(*a), M_NOWAIT);
845 		if (m == NULL)
846 			return;
847 		a = mtod(m, struct sscop_marg *);
848 		a->sig = sig;
849 		priv->stats.maa_signals++;
850 	}
851 
852 	NG_SEND_DATA_ONLY(error, priv->manage, m);
853 }
854 
855 /************************************************************/
856 /*
857  * INITIALISATION
858  */
859 
860 /*
861  * Loading and unloading of node type
862  */
863 static int
ng_sscop_mod_event(module_t mod,int event,void * data)864 ng_sscop_mod_event(module_t mod, int event, void *data)
865 {
866 	int error = 0;
867 
868 	switch (event) {
869 
870 	  case MOD_LOAD:
871 		break;
872 
873 	  case MOD_UNLOAD:
874 		break;
875 
876 	  default:
877 		error = EOPNOTSUPP;
878 		break;
879 	}
880 	return (error);
881 }
882