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$");
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
70MALLOC_DEFINE(M_NG_SSCOP, "netgraph_sscop", "netgraph sscop node");
71
72MODULE_DEPEND(ng_sscop, ngatmbase, 1, 1, 1);
73
74struct 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 */
90struct 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 */
104static const struct ng_parse_struct_field ng_sscop_param_type_info[] =
105    NG_SSCOP_PARAM_INFO;
106
107static 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 */
115static const struct ng_parse_struct_field ng_sscop_setparam_type_info[] =
116    NG_SSCOP_SETPARAM_INFO;
117
118static 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 */
126static const struct ng_parse_struct_field ng_sscop_setparam_resp_type_info[] =
127    NG_SSCOP_SETPARAM_RESP_INFO;
128
129static const struct ng_parse_type ng_sscop_setparam_resp_type = {
130	&ng_parse_struct_type,
131	ng_sscop_setparam_resp_type_info,
132};
133
134static 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
187static ng_constructor_t ng_sscop_constructor;
188static ng_shutdown_t	ng_sscop_shutdown;
189static ng_rcvmsg_t	ng_sscop_rcvmsg;
190static ng_newhook_t	ng_sscop_newhook;
191static ng_disconnect_t	ng_sscop_disconnect;
192static ng_rcvdata_t	ng_sscop_rcvlower;
193static ng_rcvdata_t	ng_sscop_rcvupper;
194static ng_rcvdata_t	ng_sscop_rcvmanage;
195
196static int ng_sscop_mod_event(module_t, int, void *);
197
198static 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};
210NETGRAPH_INIT(sscop, &ng_sscop_typestruct);
211
212static void sscop_send_manage(struct sscop *, void *, enum sscop_maasig,
213	struct SSCOP_MBUF_T *, u_int, u_int);
214static void sscop_send_upper(struct sscop *, void *, enum sscop_aasig,
215	struct SSCOP_MBUF_T *, u_int);
216static void sscop_send_lower(struct sscop *, void *,
217	struct SSCOP_MBUF_T *);
218static void sscop_verbose(struct sscop *, void *, const char *, ...)
219	__printflike(3, 4);
220
221static const struct sscop_funcs sscop_funcs = {
222	sscop_send_manage,
223	sscop_send_upper,
224	sscop_send_lower,
225	sscop_verbose
226};
227
228static void
229sscop_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 */
244static int
245ng_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}
263static int
264ng_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 */
293static int
294flow_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
306	  case NGM_HIGH_WATER_PASSED:
307		if (priv->flow) {
308			VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
309			    "flow control stopped"));
310			priv->flow = 0;
311		}
312		break;
313
314	  case NGM_LOW_WATER_PASSED:
315		window = sscop_window(priv->sscop, 0);
316		space = q->max_queuelen_packets - q->current;
317		if (space > window) {
318			VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
319			    "flow control opened window by %u messages",
320			    space - window));
321			(void)sscop_window(priv->sscop, space - window);
322		}
323		priv->flow = 1;
324		break;
325
326	  case NGM_SYNC_QUEUE_STATE:
327		if (q->high_watermark <= q->current)
328			break;
329		window = sscop_window(priv->sscop, 0);
330		if (priv->flow)
331			space = q->max_queuelen_packets - q->current;
332		else
333			space = q->high_watermark - q->current;
334		if (space > window) {
335			VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
336			    "flow control opened window by %u messages",
337			    space - window));
338			(void)sscop_window(priv->sscop, space - window);
339		}
340		priv->flow = 1;
341		break;
342
343	  default:
344		return (EINVAL);
345	}
346	return (0);
347}
348
349static int
350flow_lower(node_p node, struct ng_mesg *msg)
351{
352	struct priv *priv = NG_NODE_PRIVATE(node);
353
354	if (msg->header.arglen != sizeof(struct ngm_queue_state))
355		return (EINVAL);
356
357	switch (msg->header.cmd) {
358
359	  case NGM_HIGH_WATER_PASSED:
360		sscop_setbusy(priv->sscop, 1);
361		break;
362
363	  case NGM_LOW_WATER_PASSED:
364		sscop_setbusy(priv->sscop, 1);
365		break;
366
367	  default:
368		return (EINVAL);
369	}
370	return (0);
371}
372
373/*
374 * Produce a readable status description
375 */
376static int
377text_status(node_p node, struct priv *priv, char *arg, u_int len)
378{
379	struct sbuf sbuf;
380
381	sbuf_new(&sbuf, arg, len, 0);
382
383	if (priv->upper)
384		sbuf_printf(&sbuf, "upper hook: %s connected to %s:%s\n",
385		    NG_HOOK_NAME(priv->upper),
386		    NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->upper))),
387		    NG_HOOK_NAME(NG_HOOK_PEER(priv->upper)));
388	else
389		sbuf_printf(&sbuf, "upper hook: <not connected>\n");
390
391	if (priv->lower)
392		sbuf_printf(&sbuf, "lower hook: %s connected to %s:%s\n",
393		    NG_HOOK_NAME(priv->lower),
394		    NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->lower))),
395		    NG_HOOK_NAME(NG_HOOK_PEER(priv->lower)));
396	else
397		sbuf_printf(&sbuf, "lower hook: <not connected>\n");
398
399	if (priv->manage)
400		sbuf_printf(&sbuf, "manage hook: %s connected to %s:%s\n",
401		    NG_HOOK_NAME(priv->manage),
402		    NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->manage))),
403		    NG_HOOK_NAME(NG_HOOK_PEER(priv->manage)));
404	else
405		sbuf_printf(&sbuf, "manage hook: <not connected>\n");
406
407	sbuf_printf(&sbuf, "sscop state: %s\n",
408	    !priv->enabled ? "<disabled>" :
409	    sscop_statename(sscop_getstate(priv->sscop)));
410
411	sbuf_printf(&sbuf, "input packets:  %ju\n",
412	    (uintmax_t)priv->stats.in_packets);
413	sbuf_printf(&sbuf, "input dropped:  %ju\n",
414	    (uintmax_t)priv->stats.in_dropped);
415	sbuf_printf(&sbuf, "output packets: %ju\n",
416	    (uintmax_t)priv->stats.out_packets);
417	sbuf_printf(&sbuf, "output dropped: %ju\n",
418	    (uintmax_t)priv->stats.out_dropped);
419	sbuf_printf(&sbuf, "aa signals:     %ju\n",
420	    (uintmax_t)priv->stats.aa_signals);
421	sbuf_printf(&sbuf, "aa dropped:     %ju\n",
422	    (uintmax_t)priv->stats.aa_dropped);
423	sbuf_printf(&sbuf, "maa signals:    %ju\n",
424	    (uintmax_t)priv->stats.maa_signals);
425	sbuf_printf(&sbuf, "maa dropped:    %ju\n",
426	    (uintmax_t)priv->stats.maa_dropped);
427	sbuf_printf(&sbuf, "errors:         %ju\n",
428	    (uintmax_t)priv->stats.errors);
429	sbuf_printf(&sbuf, "data delivered: %ju\n",
430	    (uintmax_t)priv->stats.data_delivered);
431	sbuf_printf(&sbuf, "window:         %u\n",
432	    sscop_window(priv->sscop, 0));
433
434	sbuf_finish(&sbuf);
435	return (sbuf_len(&sbuf));
436}
437
438
439/*
440 * Control message received.
441 */
442static int
443ng_sscop_rcvmsg(node_p node, item_p item, hook_p lasthook)
444{
445	struct priv *priv = NG_NODE_PRIVATE(node);
446	struct ng_mesg *resp = NULL;
447	struct ng_mesg *msg;
448	int error = 0;
449
450	NGI_GET_MSG(item, msg);
451
452	switch (msg->header.typecookie) {
453
454	  case NGM_GENERIC_COOKIE:
455		switch (msg->header.cmd) {
456
457		  case NGM_TEXT_STATUS:
458			NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
459			if (resp == NULL) {
460				error = ENOMEM;
461				break;
462			}
463
464			resp->header.arglen = text_status(node, priv,
465			    (char *)resp->data, resp->header.arglen) + 1;
466			break;
467
468		  default:
469			error = EINVAL;
470			break;
471		}
472		break;
473
474	  case NGM_FLOW_COOKIE:
475		if (priv->enabled && lasthook != NULL) {
476			if (lasthook == priv->upper)
477				error = flow_upper(node, msg);
478			else if (lasthook == priv->lower)
479				error = flow_lower(node, msg);
480		}
481		break;
482
483	  case NGM_SSCOP_COOKIE:
484		switch (msg->header.cmd) {
485
486		  case NGM_SSCOP_GETPARAM:
487		    {
488			struct sscop_param *p;
489
490			NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
491			if (resp == NULL) {
492				error = ENOMEM;
493				break;
494			}
495			p = (struct sscop_param *)resp->data;
496			sscop_getparam(priv->sscop, p);
497			break;
498		    }
499
500		  case NGM_SSCOP_SETPARAM:
501		    {
502			struct ng_sscop_setparam *arg;
503			struct ng_sscop_setparam_resp *p;
504
505			if (msg->header.arglen != sizeof(*arg)) {
506				error = EINVAL;
507				break;
508			}
509			if (priv->enabled) {
510				error = EISCONN;
511				break;
512			}
513			arg = (struct ng_sscop_setparam *)msg->data;
514			NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
515			if (resp == NULL) {
516				error = ENOMEM;
517				break;
518			}
519			p = (struct ng_sscop_setparam_resp *)resp->data;
520			p->mask = arg->mask;
521			p->error = sscop_setparam(priv->sscop,
522			    &arg->param, &p->mask);
523			break;
524		    }
525
526		  case NGM_SSCOP_ENABLE:
527			if (msg->header.arglen != 0) {
528				error = EINVAL;
529				break;
530			}
531			if (priv->enabled) {
532				error = EBUSY;
533				break;
534			}
535			priv->enabled = 1;
536			priv->flow = 1;
537			memset(&priv->stats, 0, sizeof(priv->stats));
538			break;
539
540		  case NGM_SSCOP_DISABLE:
541			if (msg->header.arglen != 0) {
542				error = EINVAL;
543				break;
544			}
545			if (!priv->enabled) {
546				error = ENOTCONN;
547				break;
548			}
549			priv->enabled = 0;
550			sscop_reset(priv->sscop);
551			break;
552
553		  case NGM_SSCOP_GETDEBUG:
554			if (msg->header.arglen != 0) {
555				error = EINVAL;
556				break;
557			}
558			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
559			if(resp == NULL) {
560				error = ENOMEM;
561				break;
562			}
563			*(u_int32_t *)resp->data = sscop_getdebug(priv->sscop);
564			break;
565
566		  case NGM_SSCOP_SETDEBUG:
567			if (msg->header.arglen != sizeof(u_int32_t)) {
568				error = EINVAL;
569				break;
570			}
571			sscop_setdebug(priv->sscop, *(u_int32_t *)msg->data);
572			break;
573
574		  case NGM_SSCOP_GETSTATE:
575			if (msg->header.arglen != 0) {
576				error = EINVAL;
577				break;
578			}
579			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
580			if(resp == NULL) {
581				error = ENOMEM;
582				break;
583			}
584			*(u_int32_t *)resp->data =
585			    priv->enabled ? (sscop_getstate(priv->sscop) + 1)
586			                  : 0;
587			break;
588
589		  default:
590			error = EINVAL;
591			break;
592		}
593		break;
594
595	  default:
596		error = EINVAL;
597		break;
598	}
599
600	NG_RESPOND_MSG(error, node, item, resp);
601	NG_FREE_MSG(msg);
602
603	return (error);
604}
605
606/************************************************************/
607/*
608 * HOOK MANAGEMENT
609 */
610static int
611ng_sscop_newhook(node_p node, hook_p hook, const char *name)
612{
613	struct priv *priv = NG_NODE_PRIVATE(node);
614
615	if(strcmp(name, "upper") == 0) {
616		priv->upper = hook;
617		NG_HOOK_SET_RCVDATA(hook, ng_sscop_rcvupper);
618	} else if(strcmp(name, "lower") == 0) {
619		priv->lower = hook;
620	} else if(strcmp(name, "manage") == 0) {
621		priv->manage = hook;
622		NG_HOOK_SET_RCVDATA(hook, ng_sscop_rcvmanage);
623	} else
624		return EINVAL;
625
626	return 0;
627}
628static int
629ng_sscop_disconnect(hook_p hook)
630{
631	node_p node = NG_HOOK_NODE(hook);
632	struct priv *priv = NG_NODE_PRIVATE(node);
633
634	if(hook == priv->upper)
635		priv->upper = NULL;
636	else if(hook == priv->lower)
637		priv->lower = NULL;
638	else if(hook == priv->manage)
639		priv->manage = NULL;
640
641	if(NG_NODE_NUMHOOKS(node) == 0) {
642		if(NG_NODE_IS_VALID(node))
643			ng_rmnode_self(node);
644	} else {
645		/*
646		 * Imply a release request, if the upper layer is
647		 * disconnected.
648		 */
649		if(priv->upper == NULL && priv->lower != NULL &&
650		   priv->enabled &&
651		   sscop_getstate(priv->sscop) != SSCOP_IDLE) {
652			sscop_aasig(priv->sscop, SSCOP_RELEASE_request,
653			    NULL, 0);
654		}
655	}
656	return 0;
657}
658
659/************************************************************/
660/*
661 * DATA
662 */
663static int
664ng_sscop_rcvlower(hook_p hook, item_p item)
665{
666	struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
667	struct mbuf *m;
668
669	if (!priv->enabled) {
670		NG_FREE_ITEM(item);
671		return EINVAL;
672	}
673
674	/*
675	 * If we are disconnected at the upper layer and in the IDLE
676	 * state, drop any incoming packet.
677	 */
678	if (priv->upper != NULL || sscop_getstate(priv->sscop) != SSCOP_IDLE) {
679		NGI_GET_M(item, m);
680		priv->stats.in_packets++;
681		sscop_input(priv->sscop, m);
682	} else {
683		priv->stats.in_dropped++;
684	}
685	NG_FREE_ITEM(item);
686
687	return (0);
688}
689
690static void
691sscop_send_lower(struct sscop *sscop, void *p, struct mbuf *m)
692{
693	node_p node = (node_p)p;
694	struct priv *priv = NG_NODE_PRIVATE(node);
695	int error;
696
697	if (priv->lower == NULL) {
698		m_freem(m);
699		priv->stats.out_dropped++;
700		return;
701	}
702
703	priv->stats.out_packets++;
704	NG_SEND_DATA_ONLY(error, priv->lower, m);
705}
706
707static int
708ng_sscop_rcvupper(hook_p hook, item_p item)
709{
710	struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
711	struct sscop_arg a;
712	struct mbuf *m;
713
714	if (!priv->enabled) {
715		NG_FREE_ITEM(item);
716		return (EINVAL);
717	}
718
719	/*
720	 * If the lower layer is not connected allow to proceed.
721	 * The lower layer sending function will drop outgoing frames,
722	 * and the sscop will timeout any establish requests.
723	 */
724	NGI_GET_M(item, m);
725	NG_FREE_ITEM(item);
726
727	if (!(m->m_flags & M_PKTHDR)) {
728		printf("no pkthdr\n");
729		m_freem(m);
730		return (EINVAL);
731	}
732	if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
733		return (ENOBUFS);
734	bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a));
735	m_adj(m, sizeof(a));
736
737	return (sscop_aasig(priv->sscop, a.sig, m, a.arg));
738}
739
740static void
741sscop_send_upper(struct sscop *sscop, void *p, enum sscop_aasig sig,
742    struct SSCOP_MBUF_T *m, u_int arg)
743{
744	node_p node = (node_p)p;
745	struct priv *priv = NG_NODE_PRIVATE(node);
746	int error;
747	struct sscop_arg *a;
748
749	if (sig == SSCOP_DATA_indication && priv->flow)
750		sscop_window(priv->sscop, 1);
751
752	if (priv->upper == NULL) {
753		if (m != NULL)
754			m_freem(m);
755		priv->stats.aa_dropped++;
756		return;
757	}
758
759	priv->stats.aa_signals++;
760	if (sig == SSCOP_DATA_indication)
761		priv->stats.data_delivered++;
762
763	if (m == NULL) {
764		MGETHDR(m, M_NOWAIT, MT_DATA);
765		if (m == NULL)
766			return;
767		m->m_len = sizeof(struct sscop_arg);
768		m->m_pkthdr.len = m->m_len;
769	} else {
770		M_PREPEND(m, sizeof(struct sscop_arg), M_NOWAIT);
771		if (m == NULL)
772			return;
773	}
774	a = mtod(m, struct sscop_arg *);
775	a->sig = sig;
776	a->arg = arg;
777
778	NG_SEND_DATA_ONLY(error, priv->upper, m);
779}
780
781static int
782ng_sscop_rcvmanage(hook_p hook, item_p item)
783{
784	struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
785	struct sscop_marg a;
786	struct mbuf *m;
787
788	if (!priv->enabled) {
789		NG_FREE_ITEM(item);
790		return (EINVAL);
791	}
792
793	NGI_GET_M(item, m);
794	NG_FREE_ITEM(item);
795
796	if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
797		return (ENOBUFS);
798	bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a));
799	m_adj(m, sizeof(a));
800
801	return (sscop_maasig(priv->sscop, a.sig, m));
802}
803
804static void
805sscop_send_manage(struct sscop *sscop, void *p, enum sscop_maasig sig,
806    struct SSCOP_MBUF_T *m, u_int err, u_int cnt)
807{
808	node_p node = (node_p)p;
809	struct priv *priv = NG_NODE_PRIVATE(node);
810	int error;
811	struct sscop_merr *e;
812	struct sscop_marg *a;
813
814	if (priv->manage == NULL) {
815		if (m != NULL)
816			m_freem(m);
817		priv->stats.maa_dropped++;
818		return;
819	}
820
821	if (sig == SSCOP_MERROR_indication) {
822		MGETHDR(m, M_NOWAIT, MT_DATA);
823		if (m == NULL)
824			return;
825		m->m_len = sizeof(*e);
826		m->m_pkthdr.len = m->m_len;
827		e = mtod(m, struct sscop_merr *);
828		e->sig = sig;
829		e->err = err;
830		e->cnt = cnt;
831		priv->stats.errors++;
832	} else if (m == NULL) {
833		MGETHDR(m, M_NOWAIT, MT_DATA);
834		if (m == NULL)
835			return;
836		m->m_len = sizeof(*a);
837		m->m_pkthdr.len = m->m_len;
838		a = mtod(m, struct sscop_marg *);
839		a->sig = sig;
840		priv->stats.maa_signals++;
841	} else {
842		M_PREPEND(m, sizeof(*a), M_NOWAIT);
843		if (m == NULL)
844			return;
845		a = mtod(m, struct sscop_marg *);
846		a->sig = sig;
847		priv->stats.maa_signals++;
848	}
849
850	NG_SEND_DATA_ONLY(error, priv->manage, m);
851}
852
853/************************************************************/
854/*
855 * INITIALISATION
856 */
857
858/*
859 * Loading and unloading of node type
860 */
861static int
862ng_sscop_mod_event(module_t mod, int event, void *data)
863{
864	int error = 0;
865
866	switch (event) {
867
868	  case MOD_LOAD:
869		break;
870
871	  case MOD_UNLOAD:
872		break;
873
874	  default:
875		error = EOPNOTSUPP;
876		break;
877	}
878	return (error);
879}
880