98ef351f12
also be used as a general-purpose transport protocol above any packet layer (IP, UDP).
886 lines
20 KiB
C
886 lines
20 KiB
C
/*
|
|
* Copyright (c) 2001-2003
|
|
* Fraunhofer Institute for Open Communication Systems (FhG Fokus).
|
|
* All rights reserved.
|
|
*
|
|
* Author: Harti Brandt <harti@freebsd.org>
|
|
*
|
|
* Redistribution of this software and documentation and use in source and
|
|
* binary forms, with or without modification, are permitted provided that
|
|
* the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code or documentation must retain the above
|
|
* copyright notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
|
|
* AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
* FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* Netgraph module for ITU-T Q.2110 SSCOP.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/callout.h>
|
|
#include <sys/sbuf.h>
|
|
#include <sys/stdint.h>
|
|
#include <machine/stdarg.h>
|
|
|
|
#include <netgraph/ng_message.h>
|
|
#include <netgraph/netgraph.h>
|
|
#include <netgraph/ng_parse.h>
|
|
#include <netnatm/saal/sscopdef.h>
|
|
#include <netgraph/atm/ng_sscop.h>
|
|
#include <netgraph/atm/sscop/ng_sscop_cust.h>
|
|
#include <netnatm/saal/sscop.h>
|
|
|
|
#define DDD printf("%s: %d\n", __FUNCTION__, __LINE__)
|
|
|
|
#ifdef SSCOP_DEBUG
|
|
#define VERBOSE(P,M,F) \
|
|
do { \
|
|
if (sscop_getdebug((P)->sscop) & (M)) \
|
|
sscop_verbose F ; \
|
|
} while(0)
|
|
#else
|
|
#define VERBOSE(P,M,F)
|
|
#endif
|
|
|
|
MALLOC_DEFINE(M_NG_SSCOP, "netgraph_sscop", "netgraph sscop node");
|
|
|
|
MODULE_DEPEND(ng_sscop, ngatmbase, 1, 1, 1);
|
|
|
|
struct stats {
|
|
uint64_t in_packets;
|
|
uint64_t out_packets;
|
|
uint64_t aa_signals;
|
|
uint64_t errors;
|
|
uint64_t data_delivered;
|
|
uint64_t aa_dropped;
|
|
uint64_t maa_dropped;
|
|
uint64_t maa_signals;
|
|
uint64_t in_dropped;
|
|
uint64_t out_dropped;
|
|
};
|
|
|
|
/*
|
|
* Private data
|
|
*/
|
|
struct priv {
|
|
hook_p upper; /* SAAL interface */
|
|
hook_p lower; /* AAL5 interface */
|
|
hook_p manage; /* management interface */
|
|
|
|
struct sscop *sscop; /* sscop state */
|
|
int enabled; /* whether the protocol is enabled */
|
|
int flow; /* flow control states */
|
|
struct stats stats; /* sadistics */
|
|
};
|
|
|
|
/*
|
|
* Parse PARAM type
|
|
*/
|
|
static const struct ng_parse_struct_field ng_sscop_param_type_info[] =
|
|
NG_SSCOP_PARAM_INFO;
|
|
|
|
static const struct ng_parse_type ng_sscop_param_type = {
|
|
&ng_parse_struct_type,
|
|
ng_sscop_param_type_info
|
|
};
|
|
|
|
/*
|
|
* Parse a SET PARAM type.
|
|
*/
|
|
static const struct ng_parse_struct_field ng_sscop_setparam_type_info[] =
|
|
NG_SSCOP_SETPARAM_INFO;
|
|
|
|
static const struct ng_parse_type ng_sscop_setparam_type = {
|
|
&ng_parse_struct_type,
|
|
ng_sscop_setparam_type_info,
|
|
};
|
|
|
|
/*
|
|
* Parse a SET PARAM response
|
|
*/
|
|
static const struct ng_parse_struct_field ng_sscop_setparam_resp_type_info[] =
|
|
NG_SSCOP_SETPARAM_RESP_INFO;
|
|
|
|
static const struct ng_parse_type ng_sscop_setparam_resp_type = {
|
|
&ng_parse_struct_type,
|
|
ng_sscop_setparam_resp_type_info,
|
|
};
|
|
|
|
static const struct ng_cmdlist ng_sscop_cmdlist[] = {
|
|
{
|
|
NGM_SSCOP_COOKIE,
|
|
NGM_SSCOP_GETPARAM,
|
|
"getparam",
|
|
NULL,
|
|
&ng_sscop_param_type
|
|
},
|
|
{
|
|
NGM_SSCOP_COOKIE,
|
|
NGM_SSCOP_SETPARAM,
|
|
"setparam",
|
|
&ng_sscop_setparam_type,
|
|
&ng_sscop_setparam_resp_type
|
|
},
|
|
{
|
|
NGM_SSCOP_COOKIE,
|
|
NGM_SSCOP_ENABLE,
|
|
"enable",
|
|
NULL,
|
|
NULL
|
|
},
|
|
{
|
|
NGM_SSCOP_COOKIE,
|
|
NGM_SSCOP_DISABLE,
|
|
"disable",
|
|
NULL,
|
|
NULL
|
|
},
|
|
{
|
|
NGM_SSCOP_COOKIE,
|
|
NGM_SSCOP_GETDEBUG,
|
|
"getdebug",
|
|
NULL,
|
|
&ng_parse_hint32_type
|
|
},
|
|
{
|
|
NGM_SSCOP_COOKIE,
|
|
NGM_SSCOP_SETDEBUG,
|
|
"setdebug",
|
|
&ng_parse_hint32_type,
|
|
NULL
|
|
},
|
|
{
|
|
NGM_SSCOP_COOKIE,
|
|
NGM_SSCOP_GETSTATE,
|
|
"getstate",
|
|
NULL,
|
|
&ng_parse_uint32_type
|
|
},
|
|
{ 0 }
|
|
};
|
|
|
|
static ng_constructor_t ng_sscop_constructor;
|
|
static ng_shutdown_t ng_sscop_shutdown;
|
|
static ng_rcvmsg_t ng_sscop_rcvmsg;
|
|
static ng_newhook_t ng_sscop_newhook;
|
|
static ng_disconnect_t ng_sscop_disconnect;
|
|
static ng_rcvdata_t ng_sscop_rcvlower;
|
|
static ng_rcvdata_t ng_sscop_rcvupper;
|
|
static ng_rcvdata_t ng_sscop_rcvmanage;
|
|
|
|
static int ng_sscop_mod_event(module_t, int, void *);
|
|
|
|
static struct ng_type ng_sscop_typestruct = {
|
|
NG_ABI_VERSION,
|
|
NG_SSCOP_NODE_TYPE,
|
|
ng_sscop_mod_event, /* Module event handler (optional) */
|
|
ng_sscop_constructor, /* Node constructor */
|
|
ng_sscop_rcvmsg, /* control messages come here */
|
|
ng_sscop_shutdown, /* reset, and free resources */
|
|
ng_sscop_newhook, /* first notification of new hook */
|
|
NULL, /* findhook */
|
|
NULL, /* connect */
|
|
ng_sscop_rcvlower, /* rcvdata */
|
|
ng_sscop_disconnect, /* notify on disconnect */
|
|
ng_sscop_cmdlist,
|
|
};
|
|
NETGRAPH_INIT(sscop, &ng_sscop_typestruct);
|
|
|
|
static void sscop_send_manage(struct sscop *, void *, enum sscop_maasig,
|
|
struct SSCOP_MBUF_T *, u_int, u_int);
|
|
static void sscop_send_upper(struct sscop *, void *, enum sscop_aasig,
|
|
struct SSCOP_MBUF_T *, u_int);
|
|
static void sscop_send_lower(struct sscop *, void *,
|
|
struct SSCOP_MBUF_T *);
|
|
static void sscop_verbose(struct sscop *, void *, const char *, ...)
|
|
__printflike(3, 4);
|
|
|
|
static const struct sscop_funcs sscop_funcs = {
|
|
sscop_send_manage,
|
|
sscop_send_upper,
|
|
sscop_send_lower,
|
|
sscop_verbose
|
|
};
|
|
|
|
static void
|
|
sscop_verbose(struct sscop *sscop, void *arg, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
printf("sscop(%p): ", sscop);
|
|
vprintf(fmt, ap);
|
|
va_end(ap);
|
|
printf("\n");
|
|
}
|
|
|
|
/************************************************************/
|
|
/*
|
|
* NODE MANAGEMENT
|
|
*/
|
|
static int
|
|
ng_sscop_constructor(node_p node)
|
|
{
|
|
struct priv *p;
|
|
|
|
if ((p = malloc(sizeof(*p), M_NG_SSCOP, M_NOWAIT | M_ZERO)) == NULL)
|
|
return (ENOMEM);
|
|
|
|
if ((p->sscop = sscop_create(node, &sscop_funcs)) == NULL) {
|
|
free(p, M_NG_SSCOP);
|
|
return (ENOMEM);
|
|
}
|
|
NG_NODE_SET_PRIVATE(node, p);
|
|
|
|
/* All data message received by the node are expected to change the
|
|
* node's state. Therefor we must ensure, that we have a writer lock. */
|
|
NG_NODE_FORCE_WRITER(node);
|
|
|
|
return (0);
|
|
}
|
|
static int
|
|
ng_sscop_shutdown(node_p node)
|
|
{
|
|
struct priv *priv = NG_NODE_PRIVATE(node);
|
|
|
|
sscop_destroy(priv->sscop);
|
|
|
|
free(priv, M_NG_SSCOP);
|
|
NG_NODE_SET_PRIVATE(node, NULL);
|
|
|
|
NG_NODE_UNREF(node);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/************************************************************/
|
|
/*
|
|
* CONTROL MESSAGES
|
|
*/
|
|
/*
|
|
* Flow control message from upper layer.
|
|
* This is very experimental:
|
|
* If we get a message from the upper layer, that somebody has passed its
|
|
* high water mark, we stop updating the receive window.
|
|
* If we get a low watermark passed, then we raise the window up
|
|
* to max - current.
|
|
* If we get a queue status and it indicates a current below the
|
|
* high watermark, we unstop window updates (if they are stopped) and
|
|
* raise the window to highwater - current.
|
|
*/
|
|
static int
|
|
flow_upper(node_p node, struct ng_mesg *msg)
|
|
{
|
|
struct ngm_queue_state *q;
|
|
struct priv *priv = NG_NODE_PRIVATE(node);
|
|
u_int window, space;
|
|
|
|
if (msg->header.arglen != sizeof(struct ngm_queue_state))
|
|
return (EINVAL);
|
|
q = (struct ngm_queue_state *)msg->data;
|
|
|
|
switch (msg->header.cmd) {
|
|
|
|
case NGM_HIGH_WATER_PASSED:
|
|
if (priv->flow) {
|
|
VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
|
|
"flow control stopped"));
|
|
priv->flow = 0;
|
|
}
|
|
break;
|
|
|
|
case NGM_LOW_WATER_PASSED:
|
|
window = sscop_window(priv->sscop, 0);
|
|
space = q->max_queuelen_packets - q->current;
|
|
if (space > window) {
|
|
VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
|
|
"flow control opened window by %u messages",
|
|
space - window));
|
|
(void)sscop_window(priv->sscop, space - window);
|
|
}
|
|
priv->flow = 1;
|
|
break;
|
|
|
|
case NGM_SYNC_QUEUE_STATE:
|
|
if (q->high_watermark <= q->current)
|
|
break;
|
|
window = sscop_window(priv->sscop, 0);
|
|
if (priv->flow)
|
|
space = q->max_queuelen_packets - q->current;
|
|
else
|
|
space = q->high_watermark - q->current;
|
|
if (space > window) {
|
|
VERBOSE(priv, SSCOP_DBG_FLOW, (priv->sscop, priv,
|
|
"flow control opened window by %u messages",
|
|
space - window));
|
|
(void)sscop_window(priv->sscop, space - window);
|
|
}
|
|
priv->flow = 1;
|
|
break;
|
|
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
flow_lower(node_p node, struct ng_mesg *msg)
|
|
{
|
|
struct priv *priv = NG_NODE_PRIVATE(node);
|
|
|
|
if (msg->header.arglen != sizeof(struct ngm_queue_state))
|
|
return (EINVAL);
|
|
|
|
switch (msg->header.cmd) {
|
|
|
|
case NGM_HIGH_WATER_PASSED:
|
|
sscop_setbusy(priv->sscop, 1);
|
|
break;
|
|
|
|
case NGM_LOW_WATER_PASSED:
|
|
sscop_setbusy(priv->sscop, 1);
|
|
break;
|
|
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Produce a readable status description
|
|
*/
|
|
static int
|
|
text_status(node_p node, struct priv *priv, char *arg, u_int len)
|
|
{
|
|
struct sbuf sbuf;
|
|
|
|
sbuf_new(&sbuf, arg, len, 0);
|
|
|
|
if (priv->upper)
|
|
sbuf_printf(&sbuf, "upper hook: %s connected to %s:%s\n",
|
|
NG_HOOK_NAME(priv->upper),
|
|
NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->upper))),
|
|
NG_HOOK_NAME(NG_HOOK_PEER(priv->upper)));
|
|
else
|
|
sbuf_printf(&sbuf, "upper hook: <not connected>\n");
|
|
|
|
if (priv->lower)
|
|
sbuf_printf(&sbuf, "lower hook: %s connected to %s:%s\n",
|
|
NG_HOOK_NAME(priv->lower),
|
|
NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->lower))),
|
|
NG_HOOK_NAME(NG_HOOK_PEER(priv->lower)));
|
|
else
|
|
sbuf_printf(&sbuf, "lower hook: <not connected>\n");
|
|
|
|
if (priv->manage)
|
|
sbuf_printf(&sbuf, "manage hook: %s connected to %s:%s\n",
|
|
NG_HOOK_NAME(priv->manage),
|
|
NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->manage))),
|
|
NG_HOOK_NAME(NG_HOOK_PEER(priv->manage)));
|
|
else
|
|
sbuf_printf(&sbuf, "manage hook: <not connected>\n");
|
|
|
|
sbuf_printf(&sbuf, "sscop state: %s\n",
|
|
!priv->enabled ? "<disabled>" :
|
|
sscop_statename(sscop_getstate(priv->sscop)));
|
|
|
|
sbuf_printf(&sbuf, "input packets: %ju\n",
|
|
(uintmax_t)priv->stats.in_packets);
|
|
sbuf_printf(&sbuf, "input dropped: %ju\n",
|
|
(uintmax_t)priv->stats.in_dropped);
|
|
sbuf_printf(&sbuf, "output packets: %ju\n",
|
|
(uintmax_t)priv->stats.out_packets);
|
|
sbuf_printf(&sbuf, "output dropped: %ju\n",
|
|
(uintmax_t)priv->stats.out_dropped);
|
|
sbuf_printf(&sbuf, "aa signals: %ju\n",
|
|
(uintmax_t)priv->stats.aa_signals);
|
|
sbuf_printf(&sbuf, "aa dropped: %ju\n",
|
|
(uintmax_t)priv->stats.aa_dropped);
|
|
sbuf_printf(&sbuf, "maa signals: %ju\n",
|
|
(uintmax_t)priv->stats.maa_signals);
|
|
sbuf_printf(&sbuf, "maa dropped: %ju\n",
|
|
(uintmax_t)priv->stats.maa_dropped);
|
|
sbuf_printf(&sbuf, "errors: %ju\n",
|
|
(uintmax_t)priv->stats.errors);
|
|
sbuf_printf(&sbuf, "data delivered: %ju\n",
|
|
(uintmax_t)priv->stats.data_delivered);
|
|
sbuf_printf(&sbuf, "window: %u\n",
|
|
sscop_window(priv->sscop, 0));
|
|
|
|
sbuf_finish(&sbuf);
|
|
return (sbuf_len(&sbuf));
|
|
}
|
|
|
|
|
|
/*
|
|
* Control message received.
|
|
*/
|
|
static int
|
|
ng_sscop_rcvmsg(node_p node, item_p item, hook_p lasthook)
|
|
{
|
|
struct priv *priv = NG_NODE_PRIVATE(node);
|
|
struct ng_mesg *resp = NULL;
|
|
struct ng_mesg *msg;
|
|
int error = 0;
|
|
|
|
NGI_GET_MSG(item, msg);
|
|
|
|
switch (msg->header.typecookie) {
|
|
|
|
case NGM_GENERIC_COOKIE:
|
|
switch (msg->header.cmd) {
|
|
|
|
case NGM_TEXT_STATUS:
|
|
NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT);
|
|
if (resp == NULL) {
|
|
error = ENOMEM;
|
|
break;
|
|
}
|
|
|
|
resp->header.arglen = text_status(node, priv,
|
|
(char *)resp->data, resp->header.arglen) + 1;
|
|
break;
|
|
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case NGM_FLOW_COOKIE:
|
|
if (priv->enabled && lasthook != NULL) {
|
|
if (lasthook == priv->upper)
|
|
error = flow_upper(node, msg);
|
|
else if (lasthook == priv->lower)
|
|
error = flow_lower(node, msg);
|
|
}
|
|
break;
|
|
|
|
case NGM_SSCOP_COOKIE:
|
|
switch (msg->header.cmd) {
|
|
|
|
case NGM_SSCOP_GETPARAM:
|
|
{
|
|
struct sscop_param *p;
|
|
|
|
NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
|
|
if (resp == NULL) {
|
|
error = ENOMEM;
|
|
break;
|
|
}
|
|
p = (struct sscop_param *)resp->data;
|
|
sscop_getparam(priv->sscop, p);
|
|
break;
|
|
}
|
|
|
|
case NGM_SSCOP_SETPARAM:
|
|
{
|
|
struct ng_sscop_setparam *arg;
|
|
struct ng_sscop_setparam_resp *p;
|
|
|
|
if (msg->header.arglen != sizeof(*arg)) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
if (priv->enabled) {
|
|
error = EISCONN;
|
|
break;
|
|
}
|
|
arg = (struct ng_sscop_setparam *)msg->data;
|
|
NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT);
|
|
if (resp == NULL) {
|
|
error = ENOMEM;
|
|
break;
|
|
}
|
|
p = (struct ng_sscop_setparam_resp *)resp->data;
|
|
p->mask = arg->mask;
|
|
p->error = sscop_setparam(priv->sscop,
|
|
&arg->param, &p->mask);
|
|
break;
|
|
}
|
|
|
|
case NGM_SSCOP_ENABLE:
|
|
if (msg->header.arglen != 0) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
if (priv->enabled) {
|
|
error = EBUSY;
|
|
break;
|
|
}
|
|
priv->enabled = 1;
|
|
priv->flow = 1;
|
|
memset(&priv->stats, 0, sizeof(priv->stats));
|
|
break;
|
|
|
|
case NGM_SSCOP_DISABLE:
|
|
if (msg->header.arglen != 0) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
if (!priv->enabled) {
|
|
error = ENOTCONN;
|
|
break;
|
|
}
|
|
priv->enabled = 0;
|
|
sscop_reset(priv->sscop);
|
|
break;
|
|
|
|
case NGM_SSCOP_GETDEBUG:
|
|
if (msg->header.arglen != 0) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
|
|
if(resp == NULL) {
|
|
error = ENOMEM;
|
|
break;
|
|
}
|
|
*(u_int32_t *)resp->data = sscop_getdebug(priv->sscop);
|
|
break;
|
|
|
|
case NGM_SSCOP_SETDEBUG:
|
|
if (msg->header.arglen != sizeof(u_int32_t)) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
sscop_setdebug(priv->sscop, *(u_int32_t *)msg->data);
|
|
break;
|
|
|
|
case NGM_SSCOP_GETSTATE:
|
|
if (msg->header.arglen != 0) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
|
|
if(resp == NULL) {
|
|
error = ENOMEM;
|
|
break;
|
|
}
|
|
*(u_int32_t *)resp->data =
|
|
priv->enabled ? (sscop_getstate(priv->sscop) + 1)
|
|
: 0;
|
|
break;
|
|
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
|
|
NG_RESPOND_MSG(error, node, item, resp);
|
|
NG_FREE_MSG(msg);
|
|
|
|
return (error);
|
|
}
|
|
|
|
/************************************************************/
|
|
/*
|
|
* HOOK MANAGEMENT
|
|
*/
|
|
static int
|
|
ng_sscop_newhook(node_p node, hook_p hook, const char *name)
|
|
{
|
|
struct priv *priv = NG_NODE_PRIVATE(node);
|
|
|
|
if(strcmp(name, "upper") == 0) {
|
|
priv->upper = hook;
|
|
NG_HOOK_SET_RCVDATA(hook, ng_sscop_rcvupper);
|
|
} else if(strcmp(name, "lower") == 0) {
|
|
priv->lower = hook;
|
|
} else if(strcmp(name, "manage") == 0) {
|
|
priv->manage = hook;
|
|
NG_HOOK_SET_RCVDATA(hook, ng_sscop_rcvmanage);
|
|
} else
|
|
return EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
static int
|
|
ng_sscop_disconnect(hook_p hook)
|
|
{
|
|
node_p node = NG_HOOK_NODE(hook);
|
|
struct priv *priv = NG_NODE_PRIVATE(node);
|
|
|
|
if(hook == priv->upper)
|
|
priv->upper = NULL;
|
|
else if(hook == priv->lower)
|
|
priv->lower = NULL;
|
|
else if(hook == priv->manage)
|
|
priv->manage = NULL;
|
|
|
|
if(NG_NODE_NUMHOOKS(node) == 0) {
|
|
if(NG_NODE_IS_VALID(node))
|
|
ng_rmnode_self(node);
|
|
} else {
|
|
/*
|
|
* Imply a release request, if the upper layer is
|
|
* disconnected.
|
|
*/
|
|
if(priv->upper == NULL && priv->lower != NULL &&
|
|
priv->enabled &&
|
|
sscop_getstate(priv->sscop) != SSCOP_IDLE) {
|
|
sscop_aasig(priv->sscop, SSCOP_RELEASE_request,
|
|
NULL, 0);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/************************************************************/
|
|
/*
|
|
* DATA
|
|
*/
|
|
static int
|
|
ng_sscop_rcvlower(hook_p hook, item_p item)
|
|
{
|
|
struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
|
|
struct mbuf *m;
|
|
|
|
if (!priv->enabled) {
|
|
NG_FREE_ITEM(item);
|
|
return EINVAL;
|
|
}
|
|
|
|
/*
|
|
* If we are disconnected at the upper layer and in the IDLE
|
|
* state, drop any incoming packet.
|
|
*/
|
|
if (priv->upper != NULL || sscop_getstate(priv->sscop) != SSCOP_IDLE) {
|
|
NGI_GET_M(item, m);
|
|
priv->stats.in_packets++;
|
|
sscop_input(priv->sscop, m);
|
|
} else {
|
|
priv->stats.in_dropped++;
|
|
}
|
|
NG_FREE_ITEM(item);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
sscop_send_lower(struct sscop *sscop, void *p, struct mbuf *m)
|
|
{
|
|
node_p node = (node_p)p;
|
|
struct priv *priv = NG_NODE_PRIVATE(node);
|
|
int error;
|
|
|
|
if (priv->lower == NULL) {
|
|
m_freem(m);
|
|
priv->stats.out_dropped++;
|
|
return;
|
|
}
|
|
|
|
priv->stats.out_packets++;
|
|
NG_SEND_DATA_ONLY(error, priv->lower, m);
|
|
}
|
|
|
|
static int
|
|
ng_sscop_rcvupper(hook_p hook, item_p item)
|
|
{
|
|
struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
|
|
struct sscop_arg a;
|
|
struct mbuf *m;
|
|
|
|
if (!priv->enabled) {
|
|
NG_FREE_ITEM(item);
|
|
return (EINVAL);
|
|
}
|
|
|
|
/*
|
|
* If the lower layer is not connected allow to proceed.
|
|
* The lower layer sending function will drop outgoing frames,
|
|
* and the sscop will timeout any establish requests.
|
|
*/
|
|
NGI_GET_M(item, m);
|
|
NG_FREE_ITEM(item);
|
|
|
|
if (!(m->m_flags & M_PKTHDR)) {
|
|
printf("no pkthdr\n");
|
|
m_freem(m);
|
|
return (EINVAL);
|
|
}
|
|
if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
|
|
return (ENOBUFS);
|
|
bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a));
|
|
m_adj(m, sizeof(a));
|
|
|
|
return (sscop_aasig(priv->sscop, a.sig, m, a.arg));
|
|
}
|
|
|
|
static void
|
|
sscop_send_upper(struct sscop *sscop, void *p, enum sscop_aasig sig,
|
|
struct SSCOP_MBUF_T *m, u_int arg)
|
|
{
|
|
node_p node = (node_p)p;
|
|
struct priv *priv = NG_NODE_PRIVATE(node);
|
|
int error;
|
|
struct sscop_arg *a;
|
|
|
|
if (sig == SSCOP_DATA_indication && priv->flow)
|
|
sscop_window(priv->sscop, 1);
|
|
|
|
if (priv->upper == NULL) {
|
|
if (m != NULL)
|
|
m_freem(m);
|
|
priv->stats.aa_dropped++;
|
|
return;
|
|
}
|
|
|
|
priv->stats.aa_signals++;
|
|
if (sig == SSCOP_DATA_indication)
|
|
priv->stats.data_delivered++;
|
|
|
|
if (m == NULL) {
|
|
MGETHDR(m, M_NOWAIT, MT_DATA);
|
|
if (m == NULL)
|
|
return;
|
|
m->m_len = sizeof(struct sscop_arg);
|
|
m->m_pkthdr.len = m->m_len;
|
|
} else {
|
|
M_PREPEND(m, sizeof(struct sscop_arg), M_NOWAIT);
|
|
if (m == NULL)
|
|
return;
|
|
}
|
|
a = mtod(m, struct sscop_arg *);
|
|
a->sig = sig;
|
|
a->arg = arg;
|
|
|
|
NG_SEND_DATA_ONLY(error, priv->upper, m);
|
|
}
|
|
|
|
static int
|
|
ng_sscop_rcvmanage(hook_p hook, item_p item)
|
|
{
|
|
struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
|
|
struct sscop_marg a;
|
|
struct mbuf *m;
|
|
|
|
if (!priv->enabled) {
|
|
NG_FREE_ITEM(item);
|
|
return (EINVAL);
|
|
}
|
|
|
|
NGI_GET_M(item, m);
|
|
NG_FREE_ITEM(item);
|
|
|
|
if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL)
|
|
return (ENOBUFS);
|
|
bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a));
|
|
m_adj(m, sizeof(a));
|
|
|
|
return (sscop_maasig(priv->sscop, a.sig, m));
|
|
}
|
|
|
|
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)
|
|
{
|
|
node_p node = (node_p)p;
|
|
struct priv *priv = NG_NODE_PRIVATE(node);
|
|
int error;
|
|
struct sscop_merr *e;
|
|
struct sscop_marg *a;
|
|
|
|
if (priv->manage == NULL) {
|
|
if (m != NULL)
|
|
m_freem(m);
|
|
priv->stats.maa_dropped++;
|
|
return;
|
|
}
|
|
|
|
if (sig == SSCOP_MERROR_indication) {
|
|
MGETHDR(m, M_NOWAIT, MT_DATA);
|
|
if (m == NULL)
|
|
return;
|
|
m->m_len = sizeof(*e);
|
|
m->m_pkthdr.len = m->m_len;
|
|
e = mtod(m, struct sscop_merr *);
|
|
e->sig = sig;
|
|
e->err = err;
|
|
e->cnt = cnt;
|
|
priv->stats.errors++;
|
|
} else if (m == NULL) {
|
|
MGETHDR(m, M_NOWAIT, MT_DATA);
|
|
if (m == NULL)
|
|
return;
|
|
m->m_len = sizeof(*a);
|
|
m->m_pkthdr.len = m->m_len;
|
|
a = mtod(m, struct sscop_marg *);
|
|
a->sig = sig;
|
|
priv->stats.maa_signals++;
|
|
} else {
|
|
M_PREPEND(m, sizeof(*a), M_NOWAIT);
|
|
if (m == NULL)
|
|
return;
|
|
a = mtod(m, struct sscop_marg *);
|
|
a->sig = sig;
|
|
priv->stats.maa_signals++;
|
|
}
|
|
|
|
NG_SEND_DATA_ONLY(error, priv->manage, m);
|
|
}
|
|
|
|
/************************************************************/
|
|
/*
|
|
* INITIALISATION
|
|
*/
|
|
|
|
/*
|
|
* Loading and unloading of node type
|
|
*/
|
|
static int
|
|
ng_sscop_mod_event(module_t mod, int event, void *data)
|
|
{
|
|
int s;
|
|
int error = 0;
|
|
|
|
s = splnet();
|
|
switch (event) {
|
|
|
|
case MOD_LOAD:
|
|
break;
|
|
|
|
case MOD_UNLOAD:
|
|
break;
|
|
|
|
default:
|
|
error = EOPNOTSUPP;
|
|
break;
|
|
}
|
|
splx(s);
|
|
return (error);
|
|
}
|