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 95static const struct ng_cmdlist ng_sscfu_cmdlist[] = { 96 { 97 NGM_SSCFU_COOKIE, 98 NGM_SSCFU_GETDEFPARAM, 99 "getdefparam", 100 NULL, 101 &ng_sscfu_getdefparam_type 102 }, 103 { 104 NGM_SSCFU_COOKIE, 105 NGM_SSCFU_ENABLE, 106 "enable", 107 NULL, 108 NULL 109 }, 110 { 111 NGM_SSCFU_COOKIE, 112 NGM_SSCFU_DISABLE, 113 "disable", 114 NULL, 115 NULL 116 }, 117 { 118 NGM_SSCFU_COOKIE, 119 NGM_SSCFU_GETDEBUG, 120 "getdebug", 121 NULL, 122 &ng_parse_hint32_type 123 }, 124 { 125 NGM_SSCFU_COOKIE, 126 NGM_SSCFU_SETDEBUG, 127 "setdebug", 128 &ng_parse_hint32_type, 129 NULL 130 }, 131 { 132 NGM_SSCFU_COOKIE, 133 NGM_SSCFU_GETSTATE, 134 "getstate", 135 NULL, 136 &ng_parse_uint32_type 137 }, 138 { 0 } 139}; 140 141static ng_constructor_t ng_sscfu_constructor; 142static ng_shutdown_t ng_sscfu_shutdown; 143static ng_rcvmsg_t ng_sscfu_rcvmsg; 144static ng_newhook_t ng_sscfu_newhook; 145static ng_disconnect_t ng_sscfu_disconnect; 146static ng_rcvdata_t ng_sscfu_rcvupper; 147static ng_rcvdata_t ng_sscfu_rcvlower; 148 149static int ng_sscfu_mod_event(module_t, int, void *); 150 151static struct ng_type ng_sscfu_typestruct = { 152 .version = NG_ABI_VERSION, 153 .name = NG_SSCFU_NODE_TYPE, 154 .mod_event = ng_sscfu_mod_event, 155 .constructor = ng_sscfu_constructor, 156 .rcvmsg = ng_sscfu_rcvmsg, 157 .shutdown = ng_sscfu_shutdown, 158 .newhook = ng_sscfu_newhook, 159 .rcvdata = ng_sscfu_rcvupper, 160 .disconnect = ng_sscfu_disconnect, 161 .cmdlist = ng_sscfu_cmdlist, 162}; 163NETGRAPH_INIT(sscfu, &ng_sscfu_typestruct); 164 165static void sscfu_send_upper(struct sscfu *, void *, enum saal_sig, 166 struct mbuf *); 167static void sscfu_send_lower(struct sscfu *, void *, enum sscop_aasig, 168 struct mbuf *, u_int); 169static void sscfu_window(struct sscfu *, void *, u_int); 170static void sscfu_verbose(struct sscfu *, void *, const char *, ...) 171 __printflike(3, 4); 172 173static const struct sscfu_funcs sscfu_funcs = { 174 sscfu_send_upper, 175 sscfu_send_lower, 176 sscfu_window, 177 sscfu_verbose 178}; 179 180/************************************************************/ 181/* 182 * CONTROL MESSAGES 183 */ 184static int 185text_status(node_p node, struct priv *priv, char *arg, u_int len) 186{ 187 struct sbuf sbuf; 188 189 sbuf_new(&sbuf, arg, len, 0); 190 191 if (priv->upper) 192 sbuf_printf(&sbuf, "upper hook: %s connected to %s:%s\n", 193 NG_HOOK_NAME(priv->upper), 194 NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->upper))), 195 NG_HOOK_NAME(NG_HOOK_PEER(priv->upper))); 196 else 197 sbuf_printf(&sbuf, "upper hook: <not connected>\n"); 198 199 if (priv->lower) 200 sbuf_printf(&sbuf, "lower hook: %s connected to %s:%s\n", 201 NG_HOOK_NAME(priv->lower), 202 NG_NODE_NAME(NG_HOOK_NODE(NG_HOOK_PEER(priv->lower))), 203 NG_HOOK_NAME(NG_HOOK_PEER(priv->lower))); 204 else 205 sbuf_printf(&sbuf, "lower hook: <not connected>\n"); 206 207 sbuf_printf(&sbuf, "sscf state: %s\n", 208 priv->enabled == 0 ? "<disabled>" : 209 sscfu_statename(sscfu_getstate(priv->sscf))); 210 211 sbuf_finish(&sbuf); 212 return (sbuf_len(&sbuf)); 213} 214 215static int 216ng_sscfu_rcvmsg(node_p node, item_p item, hook_p lasthook) 217{ 218 struct priv *priv = NG_NODE_PRIVATE(node); 219 struct ng_mesg *resp = NULL; 220 struct ng_mesg *msg; 221 int error = 0; 222 223 NGI_GET_MSG(item, msg); 224 225 switch (msg->header.typecookie) { 226 case NGM_GENERIC_COOKIE: 227 switch (msg->header.cmd) { 228 case NGM_TEXT_STATUS: 229 NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT); 230 if (resp == NULL) { 231 error = ENOMEM; 232 break; 233 } 234 resp->header.arglen = text_status(node, priv, 235 (char *)resp->data, resp->header.arglen) + 1; 236 break; 237 238 default: 239 error = EINVAL; 240 break; 241 } 242 break; 243 244 case NGM_SSCFU_COOKIE: 245 switch (msg->header.cmd) { 246 case NGM_SSCFU_GETDEFPARAM: 247 { 248 struct ng_sscfu_getdefparam *p; 249 250 if (msg->header.arglen != 0) { 251 error = EINVAL; 252 break; 253 } 254 NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT); 255 if (resp == NULL) { 256 error = ENOMEM; 257 break; 258 } 259 p = (struct ng_sscfu_getdefparam *)resp->data; 260 p->mask = sscfu_getdefparam(&p->param); 261 break; 262 } 263 264 case NGM_SSCFU_ENABLE: 265 if (msg->header.arglen != 0) { 266 error = EINVAL; 267 break; 268 } 269 if (priv->enabled) { 270 error = EISCONN; 271 break; 272 } 273 priv->enabled = 1; 274 break; 275 276 case NGM_SSCFU_DISABLE: 277 if (msg->header.arglen != 0) { 278 error = EINVAL; 279 break; 280 } 281 if (!priv->enabled) { 282 error = ENOTCONN; 283 break; 284 } 285 priv->enabled = 0; 286 sscfu_reset(priv->sscf); 287 break; 288 289 case NGM_SSCFU_GETSTATE: 290 if (msg->header.arglen != 0) { 291 error = EINVAL; 292 break; 293 } 294 NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT); 295 if(resp == NULL) { 296 error = ENOMEM; 297 break; 298 } 299 *(uint32_t *)resp->data = 300 priv->enabled ? (sscfu_getstate(priv->sscf) + 1) 301 : 0; 302 break; 303 304 case NGM_SSCFU_GETDEBUG: 305 if (msg->header.arglen != 0) { 306 error = EINVAL; 307 break; 308 } 309 NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT); 310 if(resp == NULL) { 311 error = ENOMEM; 312 break; 313 } 314 *(uint32_t *)resp->data = sscfu_getdebug(priv->sscf); 315 break; 316 317 case NGM_SSCFU_SETDEBUG: 318 if (msg->header.arglen != sizeof(uint32_t)) { 319 error = EINVAL; 320 break; 321 } 322 sscfu_setdebug(priv->sscf, *(uint32_t *)msg->data); 323 break; 324 325 default: 326 error = EINVAL; 327 break; 328 } 329 break; 330 331 default: 332 error = EINVAL; 333 break; 334 } 335 336 NG_RESPOND_MSG(error, node, item, resp); 337 NG_FREE_MSG(msg); 338 339 return (error); 340} 341 342/************************************************************/ 343/* 344 * HOOK MANAGEMENT 345 */ 346static int 347ng_sscfu_newhook(node_p node, hook_p hook, const char *name) 348{ 349 struct priv *priv = NG_NODE_PRIVATE(node); 350 351 if (strcmp(name, "upper") == 0) 352 priv->upper = hook; 353 else if (strcmp(name, "lower") == 0) { 354 priv->lower = hook; 355 NG_HOOK_SET_RCVDATA(hook, ng_sscfu_rcvlower); 356 } else 357 return (EINVAL); 358 return (0); 359} 360 361static int 362ng_sscfu_disconnect(hook_p hook) 363{ 364 node_p node = NG_HOOK_NODE(hook); 365 struct priv *priv = NG_NODE_PRIVATE(node); 366 367 if (hook == priv->upper) 368 priv->upper = NULL; 369 else if (hook == priv->lower) 370 priv->lower = NULL; 371 else { 372 log(LOG_ERR, "bogus hook"); 373 return (EINVAL); 374 } 375 376 if (NG_NODE_NUMHOOKS(node) == 0) { 377 if (NG_NODE_IS_VALID(node)) 378 ng_rmnode_self(node); 379 } else { 380 /* 381 * Because there are no timeouts reset the protocol 382 * if the lower layer is disconnected. 383 */ 384 if (priv->lower == NULL && 385 priv->enabled && 386 sscfu_getstate(priv->sscf) != SSCFU_RELEASED) 387 sscfu_reset(priv->sscf); 388 } 389 return (0); 390} 391 392/************************************************************/ 393/* 394 * DATA 395 */ 396static int 397ng_sscfu_rcvupper(hook_p hook, item_p item) 398{ 399 node_p node = NG_HOOK_NODE(hook); 400 struct priv *priv = NG_NODE_PRIVATE(node); 401 struct mbuf *m; 402 struct sscfu_arg a; 403 404 if (!priv->enabled || priv->lower == NULL) { 405 NG_FREE_ITEM(item); 406 return (0); 407 } 408 409 NGI_GET_M(item, m); 410 NG_FREE_ITEM(item); 411 412 if (!(m->m_flags & M_PKTHDR)) { 413 printf("no pkthdr\n"); 414 m_freem(m); 415 return (EINVAL); 416 } 417 if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL) 418 return (ENOMEM); 419 bcopy((caddr_t)mtod(m, struct sscfu_arg *), &a, sizeof(a)); 420 m_adj(m, sizeof(a)); 421 422 return (sscfu_saalsig(priv->sscf, a.sig, m)); 423} 424 425static void 426sscfu_send_upper(struct sscfu *sscf, void *p, enum saal_sig sig, struct mbuf *m) 427{ 428 node_p node = (node_p)p; 429 struct priv *priv = NG_NODE_PRIVATE(node); 430 int error; 431 struct sscfu_arg *a; 432 433 if (priv->upper == NULL) { 434 if (m != NULL) 435 m_freem(m); 436 return; 437 } 438 if (m == NULL) { 439 MGETHDR(m, M_NOWAIT, MT_DATA); 440 if (m == NULL) 441 return; 442 m->m_len = sizeof(struct sscfu_arg); 443 m->m_pkthdr.len = m->m_len; 444 } else { 445 M_PREPEND(m, sizeof(struct sscfu_arg), M_NOWAIT); 446 if (m == NULL) 447 return; 448 } 449 a = mtod(m, struct sscfu_arg *); 450 a->sig = sig; 451 452 NG_SEND_DATA_ONLY(error, priv->upper, m); 453} 454 455static int 456ng_sscfu_rcvlower(hook_p hook, item_p item) 457{ 458 node_p node = NG_HOOK_NODE(hook); 459 struct priv *priv = NG_NODE_PRIVATE(node); 460 struct mbuf *m; 461 struct sscop_arg a; 462 463 if (!priv->enabled || priv->upper == NULL) { 464 NG_FREE_ITEM(item); 465 return (0); 466 } 467 468 NGI_GET_M(item, m); 469 NG_FREE_ITEM(item); 470 471 if (!(m->m_flags & M_PKTHDR)) { 472 printf("no pkthdr\n"); 473 m_freem(m); 474 return (EINVAL); 475 } 476 477 /* 478 * Strip of the SSCOP header. 479 */ 480 if (m->m_len < (int)sizeof(a) && (m = m_pullup(m, sizeof(a))) == NULL) 481 return (ENOMEM); 482 bcopy((caddr_t)mtod(m, struct sscop_arg *), &a, sizeof(a)); 483 m_adj(m, sizeof(a)); 484 485 sscfu_input(priv->sscf, a.sig, m, a.arg); 486 487 return (0); 488} 489 490static void 491sscfu_send_lower(struct sscfu *sscf, void *p, enum sscop_aasig sig, 492 struct mbuf *m, u_int arg) 493{ 494 node_p node = (node_p)p; 495 struct priv *priv = NG_NODE_PRIVATE(node); 496 int error; 497 struct sscop_arg *a; 498 499 if (priv->lower == NULL) { 500 if (m != NULL) 501 m_freem(m); 502 return; 503 } 504 if (m == NULL) { 505 MGETHDR(m, M_NOWAIT, MT_DATA); 506 if (m == NULL) 507 return; 508 m->m_len = sizeof(struct sscop_arg); 509 m->m_pkthdr.len = m->m_len; 510 } else { 511 M_PREPEND(m, sizeof(struct sscop_arg), M_NOWAIT); 512 if (m == NULL) 513 return; 514 } 515 a = mtod(m, struct sscop_arg *); 516 a->sig = sig; 517 a->arg = arg; 518 519 NG_SEND_DATA_ONLY(error, priv->lower, m); 520} 521 522/* 523 * Window is handled by ng_sscop so make this a NOP. 524 */ 525static void 526sscfu_window(struct sscfu *sscfu, void *arg, u_int w) 527{ 528} 529 530/************************************************************/ 531/* 532 * NODE MANAGEMENT 533 */ 534static int 535ng_sscfu_constructor(node_p node) 536{ 537 struct priv *priv; 538 539 priv = malloc(sizeof(*priv), M_NG_SSCFU, M_WAITOK | M_ZERO); 540 541 if ((priv->sscf = sscfu_create(node, &sscfu_funcs)) == NULL) { 542 free(priv, M_NG_SSCFU); 543 return (ENOMEM); 544 } 545 546 NG_NODE_SET_PRIVATE(node, priv); 547 548 return (0); 549} 550 551static int 552ng_sscfu_shutdown(node_p node) 553{ 554 struct priv *priv = NG_NODE_PRIVATE(node); 555 556 sscfu_destroy(priv->sscf); 557 558 free(priv, M_NG_SSCFU); 559 NG_NODE_SET_PRIVATE(node, NULL); 560 561 NG_NODE_UNREF(node); 562 563 return (0); 564} 565 566static void 567sscfu_verbose(struct sscfu *sscfu, void *arg, const char *fmt, ...) 568{ 569 va_list ap; 570 571 va_start(ap, fmt); 572 printf("sscfu(%p): ", sscfu); 573 vprintf(fmt, ap); 574 va_end(ap); 575 printf("\n"); 576} 577 578/************************************************************/ 579/* 580 * INITIALISATION 581 */ 582/* 583 * Loading and unloading of node type 584 */ 585static int 586ng_sscfu_mod_event(module_t mod, int event, void *data) 587{ 588 int error = 0; 589 590 switch (event) { 591 case MOD_LOAD: 592 break; 593 594 case MOD_UNLOAD: 595 break; 596 597 default: 598 error = EOPNOTSUPP; 599 break; 600 } 601 return (error); 602} 603