2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
6 * Author: Harti Brandt <harti@freebsd.org>
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:
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.
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.
30 * Netgraph module for ITU-T Q.2110 SSCOP.
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.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>
47 #include <sys/stdint.h>
48 #include <machine/stdarg.h>
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>
58 #define DDD printf("%s: %d\n", __FUNCTION__, __LINE__)
61 #define VERBOSE(P,M,F) \
63 if (sscop_getdebug((P)->sscop) & (M)) \
67 #define VERBOSE(P,M,F)
70 MALLOC_DEFINE(M_NG_SSCOP, "netgraph_sscop", "netgraph sscop node");
72 MODULE_DEPEND(ng_sscop, ngatmbase, 1, 1, 1);
79 uint64_t data_delivered;
91 hook_p upper; /* SAAL interface */
92 hook_p lower; /* AAL5 interface */
93 hook_p manage; /* management interface */
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 */
104 static const struct ng_parse_struct_field ng_sscop_param_type_info[] =
107 static const struct ng_parse_type ng_sscop_param_type = {
108 &ng_parse_struct_type,
109 ng_sscop_param_type_info
113 * Parse a SET PARAM type.
115 static const struct ng_parse_struct_field ng_sscop_setparam_type_info[] =
116 NG_SSCOP_SETPARAM_INFO;
118 static const struct ng_parse_type ng_sscop_setparam_type = {
119 &ng_parse_struct_type,
120 ng_sscop_setparam_type_info,
124 * Parse a SET PARAM response
126 static const struct ng_parse_struct_field ng_sscop_setparam_resp_type_info[] =
127 NG_SSCOP_SETPARAM_RESP_INFO;
129 static const struct ng_parse_type ng_sscop_setparam_resp_type = {
130 &ng_parse_struct_type,
131 ng_sscop_setparam_resp_type_info,
134 static const struct ng_cmdlist ng_sscop_cmdlist[] = {
146 &ng_sscop_setparam_type,
147 &ng_sscop_setparam_resp_type
168 &ng_parse_hint32_type
174 &ng_parse_hint32_type,
182 &ng_parse_uint32_type
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;
196 static int ng_sscop_mod_event(module_t, int, void *);
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,
210 NETGRAPH_INIT(sscop, &ng_sscop_typestruct);
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 *, ...)
221 static const struct sscop_funcs sscop_funcs = {
229 sscop_verbose(struct sscop *sscop, void *arg, const char *fmt, ...)
234 printf("sscop(%p): ", sscop);
240 /************************************************************/
245 ng_sscop_constructor(node_p node)
249 if ((p = malloc(sizeof(*p), M_NG_SSCOP, M_NOWAIT | M_ZERO)) == NULL)
252 if ((p->sscop = sscop_create(node, &sscop_funcs)) == NULL) {
256 NG_NODE_SET_PRIVATE(node, p);
258 /* All data message received by the node are expected to change the
259 * node's state. Therefor we must ensure, that we have a writer lock. */
260 NG_NODE_FORCE_WRITER(node);
265 ng_sscop_shutdown(node_p node)
267 struct priv *priv = NG_NODE_PRIVATE(node);
269 sscop_destroy(priv->sscop);
271 free(priv, M_NG_SSCOP);
272 NG_NODE_SET_PRIVATE(node, NULL);
279 /************************************************************/
284 * Flow control message from upper layer.
285 * This is very experimental:
286 * If we get a message from the upper layer, that somebody has passed its
287 * high water mark, we stop updating the receive window.
288 * If we get a low watermark passed, then we raise the window up
290 * If we get a queue status and it indicates a current below the
291 * high watermark, we unstop window updates (if they are stopped) and
292 * raise the window to highwater - current.
295 flow_upper(node_p node, struct ng_mesg *msg)
297 struct ngm_queue_state *q;
298 struct priv *priv = NG_NODE_PRIVATE(node);
301 if (msg->header.arglen != sizeof(struct ngm_queue_state))
303 q = (struct ngm_queue_state *)msg->data;
305 switch (msg->header.cmd) {
307 case NGM_HIGH_WATER_PASSED:
309 VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
310 "flow control stopped"));
315 case NGM_LOW_WATER_PASSED:
316 window = sscop_window(priv->sscop, 0);
317 space = q->max_queuelen_packets - q->current;
318 if (space > window) {
319 VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
320 "flow control opened window by %u messages",
322 (void)sscop_window(priv->sscop, space - window);
327 case NGM_SYNC_QUEUE_STATE:
328 if (q->high_watermark <= q->current)
330 window = sscop_window(priv->sscop, 0);
332 space = q->max_queuelen_packets - q->current;
334 space = q->high_watermark - q->current;
335 if (space > window) {
336 VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
337 "flow control opened window by %u messages",
339 (void)sscop_window(priv->sscop, space - window);
351 flow_lower(node_p node, struct ng_mesg *msg)
353 struct priv *priv = NG_NODE_PRIVATE(node);
355 if (msg->header.arglen != sizeof(struct ngm_queue_state))
358 switch (msg->header.cmd) {
360 case NGM_HIGH_WATER_PASSED:
361 sscop_setbusy(priv->sscop, 1);
364 case NGM_LOW_WATER_PASSED:
365 sscop_setbusy(priv->sscop, 1);
375 * Produce a readable status description
378 text_status(node_p node, struct priv *priv, char *arg, u_int len)
382 sbuf_new(&sbuf, arg, len, 0);
385 sbuf_printf(&sbuf, "upper hook: %s connected to %s:%s\n",
386 NG_HOOK_NAME(priv->upper),
387 NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->upper))),
388 NG_HOOK_NAME(NG_HOOK_PEER(priv->upper)));
390 sbuf_printf(&sbuf, "upper hook: <not connected>\n");
393 sbuf_printf(&sbuf, "lower hook: %s connected to %s:%s\n",
394 NG_HOOK_NAME(priv->lower),
395 NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->lower))),
396 NG_HOOK_NAME(NG_HOOK_PEER(priv->lower)));
398 sbuf_printf(&sbuf, "lower hook: <not connected>\n");
401 sbuf_printf(&sbuf, "manage hook: %s connected to %s:%s\n",
402 NG_HOOK_NAME(priv->manage),
403 NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->manage))),
404 NG_HOOK_NAME(NG_HOOK_PEER(priv->manage)));
406 sbuf_printf(&sbuf, "manage hook: <not connected>\n");
408 sbuf_printf(&sbuf, "sscop state: %s\n",
409 !priv->enabled ? "<disabled>" :
410 sscop_statename(sscop_getstate(priv->sscop)));
412 sbuf_printf(&sbuf, "input packets: %ju\n",
413 (uintmax_t)priv->stats.in_packets);
414 sbuf_printf(&sbuf, "input dropped: %ju\n",
415 (uintmax_t)priv->stats.in_dropped);
416 sbuf_printf(&sbuf, "output packets: %ju\n",
417 (uintmax_t)priv->stats.out_packets);
418 sbuf_printf(&sbuf, "output dropped: %ju\n",
419 (uintmax_t)priv->stats.out_dropped);
420 sbuf_printf(&sbuf, "aa signals: %ju\n",
421 (uintmax_t)priv->stats.aa_signals);
422 sbuf_printf(&sbuf, "aa dropped: %ju\n",
423 (uintmax_t)priv->stats.aa_dropped);
424 sbuf_printf(&sbuf, "maa signals: %ju\n",
425 (uintmax_t)priv->stats.maa_signals);
426 sbuf_printf(&sbuf, "maa dropped: %ju\n",
427 (uintmax_t)priv->stats.maa_dropped);
428 sbuf_printf(&sbuf, "errors: %ju\n",
429 (uintmax_t)priv->stats.errors);
430 sbuf_printf(&sbuf, "data delivered: %ju\n",
431 (uintmax_t)priv->stats.data_delivered);
432 sbuf_printf(&sbuf, "window: %u\n",
433 sscop_window(priv->sscop, 0));
436 return (sbuf_len(&sbuf));
441 * Control message received.
444 ng_sscop_rcvmsg(node_p node, item_p item, hook_p lasthook)
446 struct priv *priv = NG_NODE_PRIVATE(node);
447 struct ng_mesg *resp = NULL;
451 NGI_GET_MSG(item, msg);
453 switch (msg->header.typecookie) {
455 case NGM_GENERIC_COOKIE:
456 switch (msg->header.cmd) {
458 case NGM_TEXT_STATUS:
459 NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
465 resp->header.arglen = text_status(node, priv,
466 (char *)resp->data, resp->header.arglen) + 1;
475 case NGM_FLOW_COOKIE:
476 if (priv->enabled && lasthook != NULL) {
477 if (lasthook == priv->upper)
478 error = flow_upper(node, msg);
479 else if (lasthook == priv->lower)
480 error = flow_lower(node, msg);
484 case NGM_SSCOP_COOKIE:
485 switch (msg->header.cmd) {
487 case NGM_SSCOP_GETPARAM:
489 struct sscop_param *p;
491 NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
496 p = (struct sscop_param *)resp->data;
497 sscop_getparam(priv->sscop, p);
501 case NGM_SSCOP_SETPARAM:
503 struct ng_sscop_setparam *arg;
504 struct ng_sscop_setparam_resp *p;
506 if (msg->header.arglen != sizeof(*arg)) {
514 arg = (struct ng_sscop_setparam *)msg->data;
515 NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
520 p = (struct ng_sscop_setparam_resp *)resp->data;
522 p->error = sscop_setparam(priv->sscop,
523 &arg->param, &p->mask);
527 case NGM_SSCOP_ENABLE:
528 if (msg->header.arglen != 0) {
538 memset(&priv->stats, 0, sizeof(priv->stats));
541 case NGM_SSCOP_DISABLE:
542 if (msg->header.arglen != 0) {
546 if (!priv->enabled) {
551 sscop_reset(priv->sscop);
554 case NGM_SSCOP_GETDEBUG:
555 if (msg->header.arglen != 0) {
559 NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
564 *(u_int32_t *)resp->data = sscop_getdebug(priv->sscop);
567 case NGM_SSCOP_SETDEBUG:
568 if (msg->header.arglen != sizeof(u_int32_t)) {
572 sscop_setdebug(priv->sscop, *(u_int32_t *)msg->data);
575 case NGM_SSCOP_GETSTATE:
576 if (msg->header.arglen != 0) {
580 NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
585 *(u_int32_t *)resp->data =
586 priv->enabled ? (sscop_getstate(priv->sscop) + 1)
601 NG_RESPOND_MSG(error, node, item, resp);
607 /************************************************************/
612 ng_sscop_newhook(node_p node, hook_p hook, const char *name)
614 struct priv *priv = NG_NODE_PRIVATE(node);
616 if(strcmp(name, "upper") == 0) {
618 NG_HOOK_SET_RCVDATA(hook, ng_sscop_rcvupper);
619 } else if(strcmp(name, "lower") == 0) {
621 } else if(strcmp(name, "manage") == 0) {
623 NG_HOOK_SET_RCVDATA(hook, ng_sscop_rcvmanage);
630 ng_sscop_disconnect(hook_p hook)
632 node_p node = NG_HOOK_NODE(hook);
633 struct priv *priv = NG_NODE_PRIVATE(node);
635 if(hook == priv->upper)
637 else if(hook == priv->lower)
639 else if(hook == priv->manage)
642 if(NG_NODE_NUMHOOKS(node) == 0) {
643 if(NG_NODE_IS_VALID(node))
644 ng_rmnode_self(node);
647 * Imply a release request, if the upper layer is
650 if(priv->upper == NULL && priv->lower != NULL &&
652 sscop_getstate(priv->sscop) != SSCOP_IDLE) {
653 sscop_aasig(priv->sscop, SSCOP_RELEASE_request,
660 /************************************************************/
665 ng_sscop_rcvlower(hook_p hook, item_p item)
667 struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
670 if (!priv->enabled) {
676 * If we are disconnected at the upper layer and in the IDLE
677 * state, drop any incoming packet.
679 if (priv->upper != NULL || sscop_getstate(priv->sscop) != SSCOP_IDLE) {
681 priv->stats.in_packets++;
682 sscop_input(priv->sscop, m);
684 priv->stats.in_dropped++;
692 sscop_send_lower(struct sscop *sscop, void *p, struct mbuf *m)
694 node_p node = (node_p)p;
695 struct priv *priv = NG_NODE_PRIVATE(node);
698 if (priv->lower == NULL) {
700 priv->stats.out_dropped++;
704 priv->stats.out_packets++;
705 NG_SEND_DATA_ONLY(error, priv->lower, m);
709 ng_sscop_rcvupper(hook_p hook, item_p item)
711 struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
715 if (!priv->enabled) {
721 * If the lower layer is not connected allow to proceed.
722 * The lower layer sending function will drop outgoing frames,
723 * and the sscop will timeout any establish requests.
728 if (!(m->m_flags & M_PKTHDR)) {
729 printf("no pkthdr\n");
733 if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
735 bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a));
738 return (sscop_aasig(priv->sscop, a.sig, m, a.arg));
742 sscop_send_upper(struct sscop *sscop, void *p, enum sscop_aasig sig,
743 struct SSCOP_MBUF_T *m, u_int arg)
745 node_p node = (node_p)p;
746 struct priv *priv = NG_NODE_PRIVATE(node);
750 if (sig == SSCOP_DATA_indication && priv->flow)
751 sscop_window(priv->sscop, 1);
753 if (priv->upper == NULL) {
756 priv->stats.aa_dropped++;
760 priv->stats.aa_signals++;
761 if (sig == SSCOP_DATA_indication)
762 priv->stats.data_delivered++;
765 MGETHDR(m, M_NOWAIT, MT_DATA);
768 m->m_len = sizeof(struct sscop_arg);
769 m->m_pkthdr.len = m->m_len;
771 M_PREPEND(m, sizeof(struct sscop_arg), M_NOWAIT);
775 a = mtod(m, struct sscop_arg *);
779 NG_SEND_DATA_ONLY(error, priv->upper, m);
783 ng_sscop_rcvmanage(hook_p hook, item_p item)
785 struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
789 if (!priv->enabled) {
797 if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
799 bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a));
802 return (sscop_maasig(priv->sscop, a.sig, m));
806 sscop_send_manage(struct sscop *sscop, void *p, enum sscop_maasig sig,
807 struct SSCOP_MBUF_T *m, u_int err, u_int cnt)
809 node_p node = (node_p)p;
810 struct priv *priv = NG_NODE_PRIVATE(node);
812 struct sscop_merr *e;
813 struct sscop_marg *a;
815 if (priv->manage == NULL) {
818 priv->stats.maa_dropped++;
822 if (sig == SSCOP_MERROR_indication) {
823 MGETHDR(m, M_NOWAIT, MT_DATA);
826 m->m_len = sizeof(*e);
827 m->m_pkthdr.len = m->m_len;
828 e = mtod(m, struct sscop_merr *);
832 priv->stats.errors++;
833 } else if (m == NULL) {
834 MGETHDR(m, M_NOWAIT, MT_DATA);
837 m->m_len = sizeof(*a);
838 m->m_pkthdr.len = m->m_len;
839 a = mtod(m, struct sscop_marg *);
841 priv->stats.maa_signals++;
843 M_PREPEND(m, sizeof(*a), M_NOWAIT);
846 a = mtod(m, struct sscop_marg *);
848 priv->stats.maa_signals++;
851 NG_SEND_DATA_ONLY(error, priv->manage, m);
854 /************************************************************/
860 * Loading and unloading of node type
863 ng_sscop_mod_event(module_t mod, int event, void *data)