1/*- 2 * Copyright (c) 2001-2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * Author: Hartmut Brandt <harti@freebsd.org> 28 * 29 * Netgraph module for ITU-T Q.2120 UNI SSCF. 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD$"); 34 35#include <sys/param.h> 36#include <sys/systm.h> 37#include <sys/kernel.h> 38#include <sys/malloc.h> 39#include <sys/mbuf.h> 40#include <sys/errno.h> 41#include <sys/syslog.h> 42#include <sys/socket.h> 43#include <sys/socketvar.h> 44#include <sys/sbuf.h> 45#include <machine/stdarg.h> 46 47#include <netgraph/ng_message.h> 48#include <netgraph/netgraph.h> 49#include <netgraph/ng_parse.h> 50#include <netnatm/saal/sscopdef.h> 51#include <netnatm/saal/sscfudef.h> 52#include <netgraph/atm/ng_sscop.h> 53#include <netgraph/atm/ng_sscfu.h> 54#include <netgraph/atm/sscfu/ng_sscfu_cust.h> 55#include <netnatm/saal/sscfu.h> 56 57MALLOC_DEFINE(M_NG_SSCFU, "netgraph_sscfu", "netgraph uni sscf node"); 58 59MODULE_DEPEND(ng_sscfu, ngatmbase, 1, 1, 1); 60 61/* 62 * Private data 63 */ 64struct priv { 65 hook_p upper; /* SAAL interface */ 66 hook_p lower; /* SSCOP interface */ 67 struct sscfu *sscf; /* the instance */ 68 int enabled; 69}; 70 71/* 72 * PARSING 73 */ 74/* 75 * Parse PARAM type 76 */ 77static const struct ng_parse_struct_field ng_sscop_param_type_info[] = 78 NG_SSCOP_PARAM_INFO; 79 80static const struct ng_parse_type ng_sscop_param_type = { 81 &ng_parse_struct_type, 82 ng_sscop_param_type_info 83}; 84 85static const struct ng_parse_struct_field ng_sscfu_getdefparam_type_info[] = 86 NG_SSCFU_GETDEFPARAM_INFO; 87 88static const struct ng_parse_type ng_sscfu_getdefparam_type = { 89 &ng_parse_struct_type, 90 ng_sscfu_getdefparam_type_info 91}; 92 93 94static const struct ng_cmdlist ng_sscfu_cmdlist[] = { 95 { 96 NGM_SSCFU_COOKIE, 97 NGM_SSCFU_GETDEFPARAM, 98 "getdefparam", 99 NULL, 100 &ng_sscfu_getdefparam_type 101 }, 102 { 103 NGM_SSCFU_COOKIE, 104 NGM_SSCFU_ENABLE, 105 "enable", 106 NULL, 107 NULL 108 }, 109 { 110 NGM_SSCFU_COOKIE, 111 NGM_SSCFU_DISABLE, 112 "disable", 113 NULL, 114 NULL 115 }, 116 { 117 NGM_SSCFU_COOKIE, 118 NGM_SSCFU_GETDEBUG, 119 "getdebug", 120 NULL, 121 &ng_parse_hint32_type 122 }, 123 { 124 NGM_SSCFU_COOKIE, 125 NGM_SSCFU_SETDEBUG, 126 "setdebug", 127 &ng_parse_hint32_type, 128 NULL 129 }, 130 { 131 NGM_SSCFU_COOKIE, 132 NGM_SSCFU_GETSTATE, 133 "getstate", 134 NULL, 135 &ng_parse_uint32_type 136 }, 137 { 0 } 138}; 139 140static ng_constructor_t ng_sscfu_constructor; 141static ng_shutdown_t ng_sscfu_shutdown; 142static ng_rcvmsg_t ng_sscfu_rcvmsg; 143static ng_newhook_t ng_sscfu_newhook; 144static ng_disconnect_t ng_sscfu_disconnect; 145static ng_rcvdata_t ng_sscfu_rcvupper; 146static ng_rcvdata_t ng_sscfu_rcvlower; 147 148static int ng_sscfu_mod_event(module_t, int, void *); 149 150static struct ng_type ng_sscfu_typestruct = { 151 .version = NG_ABI_VERSION, 152 .name = NG_SSCFU_NODE_TYPE, 153 .mod_event = ng_sscfu_mod_event, 154 .constructor = ng_sscfu_constructor, 155 .rcvmsg = ng_sscfu_rcvmsg, 156 .shutdown = ng_sscfu_shutdown, 157 .newhook = ng_sscfu_newhook, 158 .rcvdata = ng_sscfu_rcvupper, 159 .disconnect = ng_sscfu_disconnect, 160 .cmdlist = ng_sscfu_cmdlist, 161}; 162NETGRAPH_INIT(sscfu, &ng_sscfu_typestruct); 163 164static void sscfu_send_upper(struct sscfu *, void *, enum saal_sig, 165 struct mbuf *); 166static void sscfu_send_lower(struct sscfu *, void *, enum sscop_aasig, 167 struct mbuf *, u_int); 168static void sscfu_window(struct sscfu *, void *, u_int); 169static void sscfu_verbose(struct sscfu *, void *, const char *, ...) 170 __printflike(3, 4); 171 172static const struct sscfu_funcs sscfu_funcs = { 173 sscfu_send_upper, 174 sscfu_send_lower, 175 sscfu_window, 176 sscfu_verbose 177}; 178 179/************************************************************/ 180/* 181 * CONTROL MESSAGES 182 */ 183static int 184text_status(node_p node, struct priv *priv, char *arg, u_int len) 185{ 186 struct sbuf sbuf; 187 188 sbuf_new(&sbuf, arg, len, 0); 189 190 if (priv->upper) 191 sbuf_printf(&sbuf, "upper hook: %s connected to %s:%s\n", 192 NG_HOOK_NAME(priv->upper), 193 NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->upper))), 194 NG_HOOK_NAME(NG_HOOK_PEER(priv->upper))); 195 else 196 sbuf_printf(&sbuf, "upper hook: <not connected>\n"); 197 198 if (priv->lower) 199 sbuf_printf(&sbuf, "lower hook: %s connected to %s:%s\n", 200 NG_HOOK_NAME(priv->lower), 201 NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->lower))), 202 NG_HOOK_NAME(NG_HOOK_PEER(priv->lower))); 203 else 204 sbuf_printf(&sbuf, "lower hook: <not connected>\n"); 205 206 sbuf_printf(&sbuf, "sscf state: %s\n", 207 priv->enabled == 0 ? "<disabled>" : 208 sscfu_statename(sscfu_getstate(priv->sscf))); 209 210 sbuf_finish(&sbuf); 211 return (sbuf_len(&sbuf)); 212} 213 214static int 215ng_sscfu_rcvmsg(node_p node, item_p item, hook_p lasthook) 216{ 217 struct priv *priv = NG_NODE_PRIVATE(node); 218 struct ng_mesg *resp = NULL; 219 struct ng_mesg *msg; 220 int error = 0; 221 222 NGI_GET_MSG(item, msg); 223 224 switch (msg->header.typecookie) { 225 226 case NGM_GENERIC_COOKIE: 227 switch (msg->header.cmd) { 228 229 case NGM_TEXT_STATUS: 230 NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT); 231 if (resp == NULL) { 232 error = ENOMEM; 233 break; 234 } 235 resp->header.arglen = text_status(node, priv, 236 (char *)resp->data, resp->header.arglen) + 1; 237 break; 238 239 default: 240 error = EINVAL; 241 break; 242 } 243 break; 244 245 case NGM_SSCFU_COOKIE: 246 switch (msg->header.cmd) { 247 248 case NGM_SSCFU_GETDEFPARAM: 249 { 250 struct ng_sscfu_getdefparam *p; 251 252 if (msg->header.arglen != 0) { 253 error = EINVAL; 254 break; 255 } 256 NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT); 257 if (resp == NULL) { 258 error = ENOMEM; 259 break; 260 } 261 p = (struct ng_sscfu_getdefparam *)resp->data; 262 p->mask = sscfu_getdefparam(&p->param); 263 break; 264 } 265 266 case NGM_SSCFU_ENABLE: 267 if (msg->header.arglen != 0) { 268 error = EINVAL; 269 break; 270 } 271 if (priv->enabled) { 272 error = EISCONN; 273 break; 274 } 275 priv->enabled = 1; 276 break; 277 278 case NGM_SSCFU_DISABLE: 279 if (msg->header.arglen != 0) { 280 error = EINVAL; 281 break; 282 } 283 if (!priv->enabled) { 284 error = ENOTCONN; 285 break; 286 } 287 priv->enabled = 0; 288 sscfu_reset(priv->sscf); 289 break; 290 291 case NGM_SSCFU_GETSTATE: 292 if (msg->header.arglen != 0) { 293 error = EINVAL; 294 break; 295 } 296 NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT); 297 if(resp == NULL) { 298 error = ENOMEM; 299 break; 300 } 301 *(uint32_t *)resp->data = 302 priv->enabled ? (sscfu_getstate(priv->sscf) + 1) 303 : 0; 304 break; 305 306 case NGM_SSCFU_GETDEBUG: 307 if (msg->header.arglen != 0) { 308 error = EINVAL; 309 break; 310 } 311 NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT); 312 if(resp == NULL) { 313 error = ENOMEM; 314 break; 315 } 316 *(uint32_t *)resp->data = sscfu_getdebug(priv->sscf); 317 break; 318 319 case NGM_SSCFU_SETDEBUG: 320 if (msg->header.arglen != sizeof(uint32_t)) { 321 error = EINVAL; 322 break; 323 } 324 sscfu_setdebug(priv->sscf, *(uint32_t *)msg->data); 325 break; 326 327 default: 328 error = EINVAL; 329 break; 330 } 331 break; 332 333 default: 334 error = EINVAL; 335 break; 336 } 337 338 NG_RESPOND_MSG(error, node, item, resp); 339 NG_FREE_MSG(msg); 340 341 return (error); 342} 343 344/************************************************************/ 345/* 346 * HOOK MANAGEMENT 347 */ 348static int 349ng_sscfu_newhook(node_p node, hook_p hook, const char *name) 350{ 351 struct priv *priv = NG_NODE_PRIVATE(node); 352 353 if (strcmp(name, "upper") == 0) 354 priv->upper = hook; 355 else if (strcmp(name, "lower") == 0) { 356 priv->lower = hook; 357 NG_HOOK_SET_RCVDATA(hook, ng_sscfu_rcvlower); 358 } else 359 return (EINVAL); 360 return (0); 361} 362 363static int 364ng_sscfu_disconnect(hook_p hook) 365{ 366 node_p node = NG_HOOK_NODE(hook); 367 struct priv *priv = NG_NODE_PRIVATE(node); 368 369 if (hook == priv->upper) 370 priv->upper = NULL; 371 else if (hook == priv->lower) 372 priv->lower = NULL; 373 else { 374 log(LOG_ERR, "bogus hook"); 375 return (EINVAL); 376 } 377 378 if (NG_NODE_NUMHOOKS(node) == 0) { 379 if (NG_NODE_IS_VALID(node)) 380 ng_rmnode_self(node); 381 } else { 382 /* 383 * Because there are no timeouts reset the protocol 384 * if the lower layer is disconnected. 385 */ 386 if (priv->lower == NULL && 387 priv->enabled && 388 sscfu_getstate(priv->sscf) != SSCFU_RELEASED) 389 sscfu_reset(priv->sscf); 390 } 391 return (0); 392} 393 394/************************************************************/ 395/* 396 * DATA 397 */ 398static int 399ng_sscfu_rcvupper(hook_p hook, item_p item) 400{ 401 node_p node = NG_HOOK_NODE(hook); 402 struct priv *priv = NG_NODE_PRIVATE(node); 403 struct mbuf *m; 404 struct sscfu_arg a; 405 406 if (!priv->enabled || priv->lower == NULL) { 407 NG_FREE_ITEM(item); 408 return (0); 409 } 410 411 NGI_GET_M(item, m); 412 NG_FREE_ITEM(item); 413 414 if (!(m->m_flags & M_PKTHDR)) { 415 printf("no pkthdr\n"); 416 m_freem(m); 417 return (EINVAL); 418 } 419 if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL) 420 return (ENOMEM); 421 bcopy((caddr_t)mtod(m, struct sscfu_arg *), &a, sizeof(a)); 422 m_adj(m, sizeof(a)); 423 424 return (sscfu_saalsig(priv->sscf, a.sig, m)); 425} 426 427static void 428sscfu_send_upper(struct sscfu *sscf, void *p, enum saal_sig sig, struct mbuf *m) 429{ 430 node_p node = (node_p)p; 431 struct priv *priv = NG_NODE_PRIVATE(node); 432 int error; 433 struct sscfu_arg *a; 434 435 if (priv->upper == NULL) { 436 if (m != NULL) 437 m_freem(m); 438 return; 439 } 440 if (m == NULL) { 441 MGETHDR(m, M_NOWAIT, MT_DATA); 442 if (m == NULL) 443 return; 444 m->m_len = sizeof(struct sscfu_arg); 445 m->m_pkthdr.len = m->m_len; 446 } else { 447 M_PREPEND(m, sizeof(struct sscfu_arg), M_NOWAIT); 448 if (m == NULL) 449 return; 450 } 451 a = mtod(m, struct sscfu_arg *); 452 a->sig = sig; 453 454 NG_SEND_DATA_ONLY(error, priv->upper, m); 455} 456 457static int 458ng_sscfu_rcvlower(hook_p hook, item_p item) 459{ 460 node_p node = NG_HOOK_NODE(hook); 461 struct priv *priv = NG_NODE_PRIVATE(node); 462 struct mbuf *m; 463 struct sscop_arg a; 464 465 if (!priv->enabled || priv->upper == NULL) { 466 NG_FREE_ITEM(item); 467 return (0); 468 } 469 470 NGI_GET_M(item, m); 471 NG_FREE_ITEM(item); 472 473 if (!(m->m_flags & M_PKTHDR)) { 474 printf("no pkthdr\n"); 475 m_freem(m); 476 return (EINVAL); 477 } 478 479 /* 480 * Strip of the SSCOP header. 481 */ 482 if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL) 483 return (ENOMEM); 484 bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a)); 485 m_adj(m, sizeof(a)); 486 487 sscfu_input(priv->sscf, a.sig, m, a.arg); 488 489 return (0); 490} 491 492static void 493sscfu_send_lower(struct sscfu *sscf, void *p, enum sscop_aasig sig, 494 struct mbuf *m, u_int arg) 495{ 496 node_p node = (node_p)p; 497 struct priv *priv = NG_NODE_PRIVATE(node); 498 int error; 499 struct sscop_arg *a; 500 501 if (priv->lower == NULL) { 502 if (m != NULL) 503 m_freem(m); 504 return; 505 } 506 if (m == NULL) { 507 MGETHDR(m, M_NOWAIT, MT_DATA); 508 if (m == NULL) 509 return; 510 m->m_len = sizeof(struct sscop_arg); 511 m->m_pkthdr.len = m->m_len; 512 } else { 513 M_PREPEND(m, sizeof(struct sscop_arg), M_NOWAIT); 514 if (m == NULL) 515 return; 516 } 517 a = mtod(m, struct sscop_arg *); 518 a->sig = sig; 519 a->arg = arg; 520 521 NG_SEND_DATA_ONLY(error, priv->lower, m); 522} 523 524/* 525 * Window is handled by ng_sscop so make this a NOP. 526 */ 527static void 528sscfu_window(struct sscfu *sscfu, void *arg, u_int w) 529{ 530} 531 532/************************************************************/ 533/* 534 * NODE MANAGEMENT 535 */ 536static int 537ng_sscfu_constructor(node_p node) 538{ 539 struct priv *priv; 540 541 priv = malloc(sizeof(*priv), M_NG_SSCFU, M_WAITOK | M_ZERO); 542 543 if ((priv->sscf = sscfu_create(node, &sscfu_funcs)) == NULL) { 544 free(priv, M_NG_SSCFU); 545 return (ENOMEM); 546 } 547 548 NG_NODE_SET_PRIVATE(node, priv); 549 550 return (0); 551} 552 553static int 554ng_sscfu_shutdown(node_p node) 555{ 556 struct priv *priv = NG_NODE_PRIVATE(node); 557 558 sscfu_destroy(priv->sscf); 559 560 free(priv, M_NG_SSCFU); 561 NG_NODE_SET_PRIVATE(node, NULL); 562 563 NG_NODE_UNREF(node); 564 565 return (0); 566} 567 568static void 569sscfu_verbose(struct sscfu *sscfu, void *arg, const char *fmt, ...) 570{ 571 va_list ap; 572 573 va_start(ap, fmt); 574 printf("sscfu(%p): ", sscfu); 575 vprintf(fmt, ap); 576 va_end(ap); 577 printf("\n"); 578} 579 580/************************************************************/ 581/* 582 * INITIALISATION 583 */ 584/* 585 * Loading and unloading of node type 586 */ 587static int 588ng_sscfu_mod_event(module_t mod, int event, void *data) 589{ 590 int error = 0; 591 592 switch (event) { 593 594 case MOD_LOAD: 595 break; 596 597 case MOD_UNLOAD: 598 break; 599 600 default: 601 error = EOPNOTSUPP; 602 break; 603 } 604 return (error); 605} 606