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