xref: /f-stack/freebsd/netgraph/atm/sscop/ng_sscop.c (revision 22ce4aff)
1 /*-
2  * Copyright (c) 2001-2003
3  *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *	All rights reserved.
5  *
6  * Author: Harti Brandt <[email protected]>
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$");
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 	p = malloc(sizeof(*p), M_NG_SSCOP, M_WAITOK | M_ZERO);
250 
251 	if ((p->sscop = sscop_create(node, &sscop_funcs)) == NULL) {
252 		free(p, M_NG_SSCOP);
253 		return (ENOMEM);
254 	}
255 	NG_NODE_SET_PRIVATE(node, p);
256 
257 	/* All data message received by the node are expected to change the
258 	 * node's state. Therefor we must ensure, that we have a writer lock. */
259 	NG_NODE_FORCE_WRITER(node);
260 
261 	return (0);
262 }
263 static int
ng_sscop_shutdown(node_p node)264 ng_sscop_shutdown(node_p node)
265 {
266 	struct priv *priv = NG_NODE_PRIVATE(node);
267 
268 	sscop_destroy(priv->sscop);
269 
270 	free(priv, M_NG_SSCOP);
271 	NG_NODE_SET_PRIVATE(node, NULL);
272 
273 	NG_NODE_UNREF(node);
274 
275 	return (0);
276 }
277 
278 /************************************************************/
279 /*
280  * CONTROL MESSAGES
281  */
282 /*
283  * Flow control message from upper layer.
284  * This is very experimental:
285  * If we get a message from the upper layer, that somebody has passed its
286  * high water mark, we stop updating the receive window.
287  * If we get a low watermark passed, then we raise the window up
288  * to max - current.
289  * If we get a queue status and it indicates a current below the
290  * high watermark, we unstop window updates (if they are stopped) and
291  * raise the window to highwater - current.
292  */
293 static int
flow_upper(node_p node,struct ng_mesg * msg)294 flow_upper(node_p node, struct ng_mesg *msg)
295 {
296 	struct ngm_queue_state *q;
297 	struct priv *priv = NG_NODE_PRIVATE(node);
298 	u_int window, space;
299 
300 	if (msg->header.arglen != sizeof(struct ngm_queue_state))
301 		return (EINVAL);
302 	q = (struct ngm_queue_state *)msg->data;
303 
304 	switch (msg->header.cmd) {
305 	  case NGM_HIGH_WATER_PASSED:
306 		if (priv->flow) {
307 			VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
308 			    "flow control stopped"));
309 			priv->flow = 0;
310 		}
311 		break;
312 
313 	  case NGM_LOW_WATER_PASSED:
314 		window = sscop_window(priv->sscop, 0);
315 		space = q->max_queuelen_packets - q->current;
316 		if (space > window) {
317 			VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
318 			    "flow control opened window by %u messages",
319 			    space - window));
320 			(void)sscop_window(priv->sscop, space - window);
321 		}
322 		priv->flow = 1;
323 		break;
324 
325 	  case NGM_SYNC_QUEUE_STATE:
326 		if (q->high_watermark <= q->current)
327 			break;
328 		window = sscop_window(priv->sscop, 0);
329 		if (priv->flow)
330 			space = q->max_queuelen_packets - q->current;
331 		else
332 			space = q->high_watermark - q->current;
333 		if (space > window) {
334 			VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
335 			    "flow control opened window by %u messages",
336 			    space - window));
337 			(void)sscop_window(priv->sscop, space - window);
338 		}
339 		priv->flow = 1;
340 		break;
341 
342 	  default:
343 		return (EINVAL);
344 	}
345 	return (0);
346 }
347 
348 static int
flow_lower(node_p node,struct ng_mesg * msg)349 flow_lower(node_p node, struct ng_mesg *msg)
350 {
351 	struct priv *priv = NG_NODE_PRIVATE(node);
352 
353 	if (msg->header.arglen != sizeof(struct ngm_queue_state))
354 		return (EINVAL);
355 
356 	switch (msg->header.cmd) {
357 	  case NGM_HIGH_WATER_PASSED:
358 		sscop_setbusy(priv->sscop, 1);
359 		break;
360 
361 	  case NGM_LOW_WATER_PASSED:
362 		sscop_setbusy(priv->sscop, 1);
363 		break;
364 
365 	  default:
366 		return (EINVAL);
367 	}
368 	return (0);
369 }
370 
371 /*
372  * Produce a readable status description
373  */
374 static int
text_status(node_p node,struct priv * priv,char * arg,u_int len)375 text_status(node_p node, struct priv *priv, char *arg, u_int len)
376 {
377 	struct sbuf sbuf;
378 
379 	sbuf_new(&sbuf, arg, len, 0);
380 
381 	if (priv->upper)
382 		sbuf_printf(&sbuf, "upper hook: %s connected to %s:%s\n",
383 		    NG_HOOK_NAME(priv->upper),
384 		    NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->upper))),
385 		    NG_HOOK_NAME(NG_HOOK_PEER(priv->upper)));
386 	else
387 		sbuf_printf(&sbuf, "upper hook: <not connected>\n");
388 
389 	if (priv->lower)
390 		sbuf_printf(&sbuf, "lower hook: %s connected to %s:%s\n",
391 		    NG_HOOK_NAME(priv->lower),
392 		    NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->lower))),
393 		    NG_HOOK_NAME(NG_HOOK_PEER(priv->lower)));
394 	else
395 		sbuf_printf(&sbuf, "lower hook: <not connected>\n");
396 
397 	if (priv->manage)
398 		sbuf_printf(&sbuf, "manage hook: %s connected to %s:%s\n",
399 		    NG_HOOK_NAME(priv->manage),
400 		    NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->manage))),
401 		    NG_HOOK_NAME(NG_HOOK_PEER(priv->manage)));
402 	else
403 		sbuf_printf(&sbuf, "manage hook: <not connected>\n");
404 
405 	sbuf_printf(&sbuf, "sscop state: %s\n",
406 	    !priv->enabled ? "<disabled>" :
407 	    sscop_statename(sscop_getstate(priv->sscop)));
408 
409 	sbuf_printf(&sbuf, "input packets:  %ju\n",
410 	    (uintmax_t)priv->stats.in_packets);
411 	sbuf_printf(&sbuf, "input dropped:  %ju\n",
412 	    (uintmax_t)priv->stats.in_dropped);
413 	sbuf_printf(&sbuf, "output packets: %ju\n",
414 	    (uintmax_t)priv->stats.out_packets);
415 	sbuf_printf(&sbuf, "output dropped: %ju\n",
416 	    (uintmax_t)priv->stats.out_dropped);
417 	sbuf_printf(&sbuf, "aa signals:     %ju\n",
418 	    (uintmax_t)priv->stats.aa_signals);
419 	sbuf_printf(&sbuf, "aa dropped:     %ju\n",
420 	    (uintmax_t)priv->stats.aa_dropped);
421 	sbuf_printf(&sbuf, "maa signals:    %ju\n",
422 	    (uintmax_t)priv->stats.maa_signals);
423 	sbuf_printf(&sbuf, "maa dropped:    %ju\n",
424 	    (uintmax_t)priv->stats.maa_dropped);
425 	sbuf_printf(&sbuf, "errors:         %ju\n",
426 	    (uintmax_t)priv->stats.errors);
427 	sbuf_printf(&sbuf, "data delivered: %ju\n",
428 	    (uintmax_t)priv->stats.data_delivered);
429 	sbuf_printf(&sbuf, "window:         %u\n",
430 	    sscop_window(priv->sscop, 0));
431 
432 	sbuf_finish(&sbuf);
433 	return (sbuf_len(&sbuf));
434 }
435 
436 /*
437  * Control message received.
438  */
439 static int
ng_sscop_rcvmsg(node_p node,item_p item,hook_p lasthook)440 ng_sscop_rcvmsg(node_p node, item_p item, hook_p lasthook)
441 {
442 	struct priv *priv = NG_NODE_PRIVATE(node);
443 	struct ng_mesg *resp = NULL;
444 	struct ng_mesg *msg;
445 	int error = 0;
446 
447 	NGI_GET_MSG(item, msg);
448 
449 	switch (msg->header.typecookie) {
450 	  case NGM_GENERIC_COOKIE:
451 		switch (msg->header.cmd) {
452 		  case NGM_TEXT_STATUS:
453 			NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
454 			if (resp == NULL) {
455 				error = ENOMEM;
456 				break;
457 			}
458 
459 			resp->header.arglen = text_status(node, priv,
460 			    (char *)resp->data, resp->header.arglen) + 1;
461 			break;
462 
463 		  default:
464 			error = EINVAL;
465 			break;
466 		}
467 		break;
468 
469 	  case NGM_FLOW_COOKIE:
470 		if (priv->enabled && lasthook != NULL) {
471 			if (lasthook == priv->upper)
472 				error = flow_upper(node, msg);
473 			else if (lasthook == priv->lower)
474 				error = flow_lower(node, msg);
475 		}
476 		break;
477 
478 	  case NGM_SSCOP_COOKIE:
479 		switch (msg->header.cmd) {
480 		  case NGM_SSCOP_GETPARAM:
481 		    {
482 			struct sscop_param *p;
483 
484 			NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
485 			if (resp == NULL) {
486 				error = ENOMEM;
487 				break;
488 			}
489 			p = (struct sscop_param *)resp->data;
490 			sscop_getparam(priv->sscop, p);
491 			break;
492 		    }
493 
494 		  case NGM_SSCOP_SETPARAM:
495 		    {
496 			struct ng_sscop_setparam *arg;
497 			struct ng_sscop_setparam_resp *p;
498 
499 			if (msg->header.arglen != sizeof(*arg)) {
500 				error = EINVAL;
501 				break;
502 			}
503 			if (priv->enabled) {
504 				error = EISCONN;
505 				break;
506 			}
507 			arg = (struct ng_sscop_setparam *)msg->data;
508 			NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
509 			if (resp == NULL) {
510 				error = ENOMEM;
511 				break;
512 			}
513 			p = (struct ng_sscop_setparam_resp *)resp->data;
514 			p->mask = arg->mask;
515 			p->error = sscop_setparam(priv->sscop,
516 			    &arg->param, &p->mask);
517 			break;
518 		    }
519 
520 		  case NGM_SSCOP_ENABLE:
521 			if (msg->header.arglen != 0) {
522 				error = EINVAL;
523 				break;
524 			}
525 			if (priv->enabled) {
526 				error = EBUSY;
527 				break;
528 			}
529 			priv->enabled = 1;
530 			priv->flow = 1;
531 			memset(&priv->stats, 0, sizeof(priv->stats));
532 			break;
533 
534 		  case NGM_SSCOP_DISABLE:
535 			if (msg->header.arglen != 0) {
536 				error = EINVAL;
537 				break;
538 			}
539 			if (!priv->enabled) {
540 				error = ENOTCONN;
541 				break;
542 			}
543 			priv->enabled = 0;
544 			sscop_reset(priv->sscop);
545 			break;
546 
547 		  case NGM_SSCOP_GETDEBUG:
548 			if (msg->header.arglen != 0) {
549 				error = EINVAL;
550 				break;
551 			}
552 			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
553 			if(resp == NULL) {
554 				error = ENOMEM;
555 				break;
556 			}
557 			*(u_int32_t *)resp->data = sscop_getdebug(priv->sscop);
558 			break;
559 
560 		  case NGM_SSCOP_SETDEBUG:
561 			if (msg->header.arglen != sizeof(u_int32_t)) {
562 				error = EINVAL;
563 				break;
564 			}
565 			sscop_setdebug(priv->sscop, *(u_int32_t *)msg->data);
566 			break;
567 
568 		  case NGM_SSCOP_GETSTATE:
569 			if (msg->header.arglen != 0) {
570 				error = EINVAL;
571 				break;
572 			}
573 			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
574 			if(resp == NULL) {
575 				error = ENOMEM;
576 				break;
577 			}
578 			*(u_int32_t *)resp->data =
579 			    priv->enabled ? (sscop_getstate(priv->sscop) + 1)
580 			                  : 0;
581 			break;
582 
583 		  default:
584 			error = EINVAL;
585 			break;
586 		}
587 		break;
588 
589 	  default:
590 		error = EINVAL;
591 		break;
592 	}
593 
594 	NG_RESPOND_MSG(error, node, item, resp);
595 	NG_FREE_MSG(msg);
596 
597 	return (error);
598 }
599 
600 /************************************************************/
601 /*
602  * HOOK MANAGEMENT
603  */
604 static int
ng_sscop_newhook(node_p node,hook_p hook,const char * name)605 ng_sscop_newhook(node_p node, hook_p hook, const char *name)
606 {
607 	struct priv *priv = NG_NODE_PRIVATE(node);
608 
609 	if(strcmp(name, "upper") == 0) {
610 		priv->upper = hook;
611 		NG_HOOK_SET_RCVDATA(hook, ng_sscop_rcvupper);
612 	} else if(strcmp(name, "lower") == 0) {
613 		priv->lower = hook;
614 	} else if(strcmp(name, "manage") == 0) {
615 		priv->manage = hook;
616 		NG_HOOK_SET_RCVDATA(hook, ng_sscop_rcvmanage);
617 	} else
618 		return EINVAL;
619 
620 	return 0;
621 }
622 static int
ng_sscop_disconnect(hook_p hook)623 ng_sscop_disconnect(hook_p hook)
624 {
625 	node_p node = NG_HOOK_NODE(hook);
626 	struct priv *priv = NG_NODE_PRIVATE(node);
627 
628 	if(hook == priv->upper)
629 		priv->upper = NULL;
630 	else if(hook == priv->lower)
631 		priv->lower = NULL;
632 	else if(hook == priv->manage)
633 		priv->manage = NULL;
634 
635 	if(NG_NODE_NUMHOOKS(node) == 0) {
636 		if(NG_NODE_IS_VALID(node))
637 			ng_rmnode_self(node);
638 	} else {
639 		/*
640 		 * Imply a release request, if the upper layer is
641 		 * disconnected.
642 		 */
643 		if(priv->upper == NULL && priv->lower != NULL &&
644 		   priv->enabled &&
645 		   sscop_getstate(priv->sscop) != SSCOP_IDLE) {
646 			sscop_aasig(priv->sscop, SSCOP_RELEASE_request,
647 			    NULL, 0);
648 		}
649 	}
650 	return 0;
651 }
652 
653 /************************************************************/
654 /*
655  * DATA
656  */
657 static int
ng_sscop_rcvlower(hook_p hook,item_p item)658 ng_sscop_rcvlower(hook_p hook, item_p item)
659 {
660 	struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
661 	struct mbuf *m;
662 
663 	if (!priv->enabled) {
664 		NG_FREE_ITEM(item);
665 		return EINVAL;
666 	}
667 
668 	/*
669 	 * If we are disconnected at the upper layer and in the IDLE
670 	 * state, drop any incoming packet.
671 	 */
672 	if (priv->upper != NULL || sscop_getstate(priv->sscop) != SSCOP_IDLE) {
673 		NGI_GET_M(item, m);
674 		priv->stats.in_packets++;
675 		sscop_input(priv->sscop, m);
676 	} else {
677 		priv->stats.in_dropped++;
678 	}
679 	NG_FREE_ITEM(item);
680 
681 	return (0);
682 }
683 
684 static void
sscop_send_lower(struct sscop * sscop,void * p,struct mbuf * m)685 sscop_send_lower(struct sscop *sscop, void *p, struct mbuf *m)
686 {
687 	node_p node = (node_p)p;
688 	struct priv *priv = NG_NODE_PRIVATE(node);
689 	int error;
690 
691 	if (priv->lower == NULL) {
692 		m_freem(m);
693 		priv->stats.out_dropped++;
694 		return;
695 	}
696 
697 	priv->stats.out_packets++;
698 	NG_SEND_DATA_ONLY(error, priv->lower, m);
699 }
700 
701 static int
ng_sscop_rcvupper(hook_p hook,item_p item)702 ng_sscop_rcvupper(hook_p hook, item_p item)
703 {
704 	struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
705 	struct sscop_arg a;
706 	struct mbuf *m;
707 
708 	if (!priv->enabled) {
709 		NG_FREE_ITEM(item);
710 		return (EINVAL);
711 	}
712 
713 	/*
714 	 * If the lower layer is not connected allow to proceed.
715 	 * The lower layer sending function will drop outgoing frames,
716 	 * and the sscop will timeout any establish requests.
717 	 */
718 	NGI_GET_M(item, m);
719 	NG_FREE_ITEM(item);
720 
721 	if (!(m->m_flags & M_PKTHDR)) {
722 		printf("no pkthdr\n");
723 		m_freem(m);
724 		return (EINVAL);
725 	}
726 	if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
727 		return (ENOBUFS);
728 	bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a));
729 	m_adj(m, sizeof(a));
730 
731 	return (sscop_aasig(priv->sscop, a.sig, m, a.arg));
732 }
733 
734 static void
sscop_send_upper(struct sscop * sscop,void * p,enum sscop_aasig sig,struct SSCOP_MBUF_T * m,u_int arg)735 sscop_send_upper(struct sscop *sscop, void *p, enum sscop_aasig sig,
736     struct SSCOP_MBUF_T *m, u_int arg)
737 {
738 	node_p node = (node_p)p;
739 	struct priv *priv = NG_NODE_PRIVATE(node);
740 	int error;
741 	struct sscop_arg *a;
742 
743 	if (sig == SSCOP_DATA_indication && priv->flow)
744 		sscop_window(priv->sscop, 1);
745 
746 	if (priv->upper == NULL) {
747 		if (m != NULL)
748 			m_freem(m);
749 		priv->stats.aa_dropped++;
750 		return;
751 	}
752 
753 	priv->stats.aa_signals++;
754 	if (sig == SSCOP_DATA_indication)
755 		priv->stats.data_delivered++;
756 
757 	if (m == NULL) {
758 		MGETHDR(m, M_NOWAIT, MT_DATA);
759 		if (m == NULL)
760 			return;
761 		m->m_len = sizeof(struct sscop_arg);
762 		m->m_pkthdr.len = m->m_len;
763 	} else {
764 		M_PREPEND(m, sizeof(struct sscop_arg), M_NOWAIT);
765 		if (m == NULL)
766 			return;
767 	}
768 	a = mtod(m, struct sscop_arg *);
769 	a->sig = sig;
770 	a->arg = arg;
771 
772 	NG_SEND_DATA_ONLY(error, priv->upper, m);
773 }
774 
775 static int
ng_sscop_rcvmanage(hook_p hook,item_p item)776 ng_sscop_rcvmanage(hook_p hook, item_p item)
777 {
778 	struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
779 	struct sscop_marg a;
780 	struct mbuf *m;
781 
782 	if (!priv->enabled) {
783 		NG_FREE_ITEM(item);
784 		return (EINVAL);
785 	}
786 
787 	NGI_GET_M(item, m);
788 	NG_FREE_ITEM(item);
789 
790 	if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
791 		return (ENOBUFS);
792 	bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a));
793 	m_adj(m, sizeof(a));
794 
795 	return (sscop_maasig(priv->sscop, a.sig, m));
796 }
797 
798 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)799 sscop_send_manage(struct sscop *sscop, void *p, enum sscop_maasig sig,
800     struct SSCOP_MBUF_T *m, u_int err, u_int cnt)
801 {
802 	node_p node = (node_p)p;
803 	struct priv *priv = NG_NODE_PRIVATE(node);
804 	int error;
805 	struct sscop_merr *e;
806 	struct sscop_marg *a;
807 
808 	if (priv->manage == NULL) {
809 		if (m != NULL)
810 			m_freem(m);
811 		priv->stats.maa_dropped++;
812 		return;
813 	}
814 
815 	if (sig == SSCOP_MERROR_indication) {
816 		MGETHDR(m, M_NOWAIT, MT_DATA);
817 		if (m == NULL)
818 			return;
819 		m->m_len = sizeof(*e);
820 		m->m_pkthdr.len = m->m_len;
821 		e = mtod(m, struct sscop_merr *);
822 		e->sig = sig;
823 		e->err = err;
824 		e->cnt = cnt;
825 		priv->stats.errors++;
826 	} else if (m == NULL) {
827 		MGETHDR(m, M_NOWAIT, MT_DATA);
828 		if (m == NULL)
829 			return;
830 		m->m_len = sizeof(*a);
831 		m->m_pkthdr.len = m->m_len;
832 		a = mtod(m, struct sscop_marg *);
833 		a->sig = sig;
834 		priv->stats.maa_signals++;
835 	} else {
836 		M_PREPEND(m, sizeof(*a), M_NOWAIT);
837 		if (m == NULL)
838 			return;
839 		a = mtod(m, struct sscop_marg *);
840 		a->sig = sig;
841 		priv->stats.maa_signals++;
842 	}
843 
844 	NG_SEND_DATA_ONLY(error, priv->manage, m);
845 }
846 
847 /************************************************************/
848 /*
849  * INITIALISATION
850  */
851 
852 /*
853  * Loading and unloading of node type
854  */
855 static int
ng_sscop_mod_event(module_t mod,int event,void * data)856 ng_sscop_mod_event(module_t mod, int event, void *data)
857 {
858 	int error = 0;
859 
860 	switch (event) {
861 	  case MOD_LOAD:
862 		break;
863 
864 	  case MOD_UNLOAD:
865 		break;
866 
867 	  default:
868 		error = EOPNOTSUPP;
869 		break;
870 	}
871 	return (error);
872 }
873