ng_atm.c revision 116808
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 to connect NATM interfaces to netgraph. 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD: head/sys/netgraph/atm/ng_atm.c 116808 2003-06-25 13:20:19Z harti $"); 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 <sys/ioccom.h> 46#include <sys/sysctl.h> 47 48#include <net/if.h> 49#include <net/if_types.h> 50#include <net/if_arp.h> 51#include <net/if_var.h> 52#include <net/if_media.h> 53#include <net/if_atm.h> 54 55#include <netgraph/ng_message.h> 56#include <netgraph/netgraph.h> 57#include <netgraph/ng_parse.h> 58#include <netgraph/atm/ng_atm.h> 59 60/* 61 * Hooks in the NATM code 62 */ 63extern void (*ng_atm_attach_p)(struct ifnet *); 64extern void (*ng_atm_detach_p)(struct ifnet *); 65extern int (*ng_atm_output_p)(struct ifnet *, struct mbuf **); 66extern void (*ng_atm_input_p)(struct ifnet *, struct mbuf **, 67 struct atm_pseudohdr *, void *); 68extern void (*ng_atm_input_orphan_p)(struct ifnet *, struct mbuf *, 69 struct atm_pseudohdr *, void *); 70extern void (*ng_atm_message_p)(struct ifnet *, uint32_t, uint32_t); 71 72/* 73 * Sysctl stuff. 74 */ 75SYSCTL_NODE(_net_graph, OID_AUTO, atm, CTLFLAG_RW, 0, "atm related stuff"); 76 77#ifdef NGATM_DEBUG 78static int allow_shutdown; 79 80SYSCTL_INT(_net_graph_atm, OID_AUTO, allow_shutdown, CTLFLAG_RW, 81 &allow_shutdown, 0, "allow ng_atm nodes to shutdown"); 82#endif 83 84/* 85 * Hook private data 86 */ 87struct ngvcc { 88 uint16_t vpi; /* VPI of this hook */ 89 uint16_t vci; /* VCI of this hook, 0 if none */ 90 uint32_t flags; /* private flags */ 91 hook_p hook; /* the connected hook */ 92 93 LIST_ENTRY(ngvcc) link; 94}; 95 96/* 97 * Node private data 98 */ 99struct priv { 100 struct ifnet *ifp; /* the ATM interface */ 101 hook_p input; /* raw input hook */ 102 hook_p orphans; /* packets to nowhere */ 103 hook_p output; /* catch output packets */ 104 hook_p manage; /* has also entry in vccs */ 105 uint64_t in_packets; 106 uint64_t in_errors; 107 uint64_t out_packets; 108 uint64_t out_errors; 109 110 LIST_HEAD(, ngvcc) vccs; 111}; 112 113/* 114 * Parse carrier state 115 */ 116static const struct ng_parse_struct_field ng_atm_carrier_change_info[] = 117 NGM_ATM_CARRIER_CHANGE_INFO; 118static const struct ng_parse_type ng_atm_carrier_change_type = { 119 &ng_parse_struct_type, 120 &ng_atm_carrier_change_info 121}; 122 123/* 124 * Parse the configuration structure ng_atm_config 125 */ 126static const struct ng_parse_struct_field ng_atm_config_type_info[] = 127 NGM_ATM_CONFIG_INFO; 128 129static const struct ng_parse_type ng_atm_config_type = { 130 &ng_parse_struct_type, 131 &ng_atm_config_type_info 132}; 133 134/* 135 * Parse a single vcc structure and a variable array of these ng_atm_vccs 136 */ 137static const struct ng_parse_struct_field ng_atm_tparam_type_info[] = 138 NGM_ATM_TPARAM_INFO; 139static const struct ng_parse_type ng_atm_tparam_type = { 140 &ng_parse_struct_type, 141 &ng_atm_tparam_type_info 142}; 143static const struct ng_parse_struct_field ng_atm_vcc_type_info[] = 144 NGM_ATM_VCC_INFO; 145static const struct ng_parse_type ng_atm_vcc_type = { 146 &ng_parse_struct_type, 147 &ng_atm_vcc_type_info 148}; 149 150 151static int 152ng_atm_vccarray_getlen(const struct ng_parse_type *type, 153 const u_char *start, const u_char *buf) 154{ 155 const struct atmio_vcctable *vp; 156 157 vp = (const struct atmio_vcctable *) 158 (buf - offsetof(struct atmio_vcctable, vccs)); 159 160 return (vp->count); 161} 162static const struct ng_parse_array_info ng_atm_vccarray_info = 163 NGM_ATM_VCCARRAY_INFO; 164static const struct ng_parse_type ng_atm_vccarray_type = { 165 &ng_parse_array_type, 166 &ng_atm_vccarray_info 167}; 168 169 170static const struct ng_parse_struct_field ng_atm_vcctable_type_info[] = 171 NGM_ATM_VCCTABLE_INFO; 172 173static const struct ng_parse_type ng_atm_vcctable_type = { 174 &ng_parse_struct_type, 175 &ng_atm_vcctable_type_info 176}; 177 178/* 179 * Parse CPCS INIT structure ng_atm_cpcs_init 180 */ 181static const struct ng_parse_struct_field ng_atm_cpcs_init_type_info[] = 182 NGM_ATM_CPCS_INIT_INFO; 183 184static const struct ng_parse_type ng_atm_cpcs_init_type = { 185 &ng_parse_struct_type, 186 &ng_atm_cpcs_init_type_info 187}; 188 189/* 190 * Parse CPCS TERM structure ng_atm_cpcs_term 191 */ 192static const struct ng_parse_struct_field ng_atm_cpcs_term_type_info[] = 193 NGM_ATM_CPCS_TERM_INFO; 194 195static const struct ng_parse_type ng_atm_cpcs_term_type = { 196 &ng_parse_struct_type, 197 &ng_atm_cpcs_term_type_info 198}; 199 200/* 201 * Parse statistic struct 202 */ 203static const struct ng_parse_struct_field ng_atm_stats_type_info[] = 204 NGM_ATM_STATS_INFO; 205 206static const struct ng_parse_type ng_atm_stats_type = { 207 &ng_parse_struct_type, 208 &ng_atm_stats_type_info 209}; 210 211static const struct ng_cmdlist ng_atm_cmdlist[] = { 212 { 213 NGM_ATM_COOKIE, 214 NGM_ATM_GET_IFNAME, 215 "getifname", 216 NULL, 217 &ng_parse_string_type 218 }, 219 { 220 NGM_ATM_COOKIE, 221 NGM_ATM_GET_CONFIG, 222 "getconfig", 223 NULL, 224 &ng_atm_config_type 225 }, 226 { 227 NGM_ATM_COOKIE, 228 NGM_ATM_GET_VCCS, 229 "getvccs", 230 NULL, 231 &ng_atm_vcctable_type 232 }, 233 { 234 NGM_ATM_COOKIE, 235 NGM_ATM_CPCS_INIT, 236 "cpcsinit", 237 &ng_atm_cpcs_init_type, 238 NULL 239 }, 240 { 241 NGM_ATM_COOKIE, 242 NGM_ATM_CPCS_TERM, 243 "cpcsterm", 244 &ng_atm_cpcs_term_type, 245 NULL 246 }, 247 { 248 NGM_ATM_COOKIE, 249 NGM_ATM_CARRIER_CHANGE, 250 "carrier", 251 &ng_atm_carrier_change_type, 252 &ng_atm_carrier_change_type, 253 }, 254 { 255 NGM_ATM_COOKIE, 256 NGM_ATM_GET_VCC, 257 "getvcc", 258 &ng_parse_hookbuf_type, 259 &ng_atm_vcc_type 260 }, 261 { 262 NGM_ATM_COOKIE, 263 NGM_ATM_GET_VCCID, 264 "getvccid", 265 &ng_atm_vcc_type, 266 &ng_atm_vcc_type 267 }, 268 { 269 NGM_ATM_COOKIE, 270 NGM_ATM_GET_STATS, 271 "getstats", 272 NULL, 273 &ng_atm_stats_type 274 }, 275 { 0 } 276}; 277 278static int ng_atm_mod_event(module_t, int, void *); 279 280static ng_constructor_t ng_atm_constructor; 281static ng_shutdown_t ng_atm_shutdown; 282static ng_rcvmsg_t ng_atm_rcvmsg; 283static ng_newhook_t ng_atm_newhook; 284static ng_connect_t ng_atm_connect; 285static ng_disconnect_t ng_atm_disconnect; 286static ng_rcvdata_t ng_atm_rcvdata; 287static ng_rcvdata_t ng_atm_rcvdrop; 288 289static struct ng_type ng_atm_typestruct = { 290 NG_ABI_VERSION, 291 NG_ATM_NODE_TYPE, 292 ng_atm_mod_event, /* Module event handler (optional) */ 293 ng_atm_constructor, /* Node constructor */ 294 ng_atm_rcvmsg, /* control messages come here */ 295 ng_atm_shutdown, /* reset, and free resources */ 296 ng_atm_newhook, /* first notification of new hook */ 297 NULL, /* findhook */ 298 ng_atm_connect, /* connect */ 299 ng_atm_rcvdata, /* rcvdata */ 300 ng_atm_disconnect, /* notify on disconnect */ 301 ng_atm_cmdlist, 302}; 303NETGRAPH_INIT(atm, &ng_atm_typestruct); 304 305static const struct { 306 u_int media; 307 const char *name; 308} atmmedia[] = IFM_SUBTYPE_ATM_DESCRIPTIONS; 309 310 311#define IFP2NG(IFP) ((node_p)((struct ifatm *)(IFP))->ngpriv) 312 313#define IFFLAGS "\020\001UP\002BROADCAST\003DEBUG\004LOOPBACK" \ 314 "\005POINTOPOINT\006SMART\007RUNNING\010NOARP" \ 315 "\011PROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX" \ 316 "\015LINK0\016LINK1\017LINK2\020MULTICAST" 317 318 319/************************************************************/ 320/* 321 * INPUT 322 */ 323/* 324 * A packet is received from an interface. 325 * If we have an input hook, prepend the pseudoheader to the data and 326 * deliver it out to that hook. If not, look whether it is destined for 327 * use. If so locate the appropriate hook, deliver the packet without the 328 * header and we are done. If it is not for us, leave it alone. 329 */ 330static void 331ng_atm_input(struct ifnet *ifp, struct mbuf **mp, 332 struct atm_pseudohdr *ah, void *rxhand) 333{ 334 node_p node = IFP2NG(ifp); 335 struct priv *priv; 336 const struct ngvcc *vcc; 337 int error; 338 339 if (node == NULL) 340 return; 341 priv = NG_NODE_PRIVATE(node); 342 if (priv->input != NULL) { 343 /* 344 * Prepend the atm_pseudoheader. 345 */ 346 M_PREPEND(*mp, sizeof(*ah), M_DONTWAIT); 347 if (*mp == NULL) 348 return; 349 memcpy(mtod(*mp, struct atm_pseudohdr *), ah, sizeof(*ah)); 350 NG_SEND_DATA_ONLY(error, priv->input, *mp); 351 if (error == 0) { 352 priv->in_packets++; 353 *mp = NULL; 354 } else { 355#ifdef NGATM_DEBUG 356 printf("%s: error=%d\n", __func__, error); 357#endif 358 priv->in_errors++; 359 } 360 return; 361 } 362 if ((ATM_PH_FLAGS(ah) & ATMIO_FLAG_NG) == 0) 363 return; 364 365 vcc = (struct ngvcc *)rxhand; 366 367 NG_SEND_DATA_ONLY(error, vcc->hook, *mp); 368 if (error == 0) { 369 priv->in_packets++; 370 *mp = NULL; 371 } else { 372#ifdef NGATM_DEBUG 373 printf("%s: error=%d\n", __func__, error); 374#endif 375 priv->in_errors++; 376 } 377} 378 379/* 380 * ATM packet is about to be output. The atm_pseudohdr is already prepended. 381 * If the hook is set, reroute the packet to the hook. 382 */ 383static int 384ng_atm_output(struct ifnet *ifp, struct mbuf **mp) 385{ 386 const node_p node = IFP2NG(ifp); 387 const struct priv *priv; 388 int error = 0; 389 390 if (node == NULL) 391 return (0); 392 priv = NG_NODE_PRIVATE(node); 393 if (priv->output) { 394 NG_SEND_DATA_ONLY(error, priv->output, *mp); 395 *mp = NULL; 396 } 397 398 return (error); 399} 400 401/* 402 * Well, this doesn't make much sense for ATM. 403 */ 404static void 405ng_atm_input_orphans(struct ifnet *ifp, struct mbuf *m, 406 struct atm_pseudohdr *ah, void *rxhand) 407{ 408 node_p node = IFP2NG(ifp); 409 struct priv *priv; 410 int error; 411 412 if (node == NULL) { 413 m_freem(m); 414 return; 415 } 416 priv = NG_NODE_PRIVATE(node); 417 if (priv->orphans == NULL) { 418 m_freem(m); 419 return; 420 } 421 /* 422 * Prepend the atm_pseudoheader. 423 */ 424 M_PREPEND(m, sizeof(*ah), M_DONTWAIT); 425 if (m == NULL) 426 return; 427 memcpy(mtod(m, struct atm_pseudohdr *), ah, sizeof(*ah)); 428 NG_SEND_DATA_ONLY(error, priv->orphans, m); 429 if (error == 0) 430 priv->in_packets++; 431 else { 432 priv->in_errors++; 433#ifdef NGATM_DEBUG 434 printf("%s: error=%d\n", __func__, error); 435#endif 436 } 437} 438 439/************************************************************/ 440/* 441 * OUTPUT 442 */ 443static int 444ng_atm_rcvdata(hook_p hook, item_p item) 445{ 446 node_p node = NG_HOOK_NODE(hook); 447 struct priv *priv = NG_NODE_PRIVATE(node); 448 const struct ngvcc *vcc = NG_HOOK_PRIVATE(hook); 449 struct mbuf *m; 450 struct atm_pseudohdr *aph; 451 int error; 452 453 if (vcc->vci == 0) { 454 NG_FREE_ITEM(item); 455 return (ENOTCONN); 456 } 457 458 NGI_GET_M(item, m); 459 NG_FREE_ITEM(item); 460 461 /* 462 * Prepend pseudo-hdr. Drivers don't care about the flags. 463 */ 464 M_PREPEND(m, sizeof(*aph), M_DONTWAIT); 465 if (m == NULL) { 466 NG_FREE_M(m); 467 return (ENOMEM); 468 } 469 aph = mtod(m, struct atm_pseudohdr *); 470 ATM_PH_VPI(aph) = vcc->vpi; 471 ATM_PH_SETVCI(aph, vcc->vci); 472 ATM_PH_FLAGS(aph) = 0; 473 474 if ((error = atm_output(priv->ifp, m, NULL, NULL)) == 0) 475 priv->out_packets++; 476 else 477 priv->out_errors++; 478 return (error); 479} 480 481static int 482ng_atm_rcvdrop(hook_p hook, item_p item) 483{ 484 NG_FREE_ITEM(item); 485 return (0); 486} 487 488 489/************************************************************ 490 * 491 * Message from driver. 492 */ 493#ifdef notyet 494static void 495ng_atm_message_func(node_p node, hook_p hook, void *arg1, int arg2) 496{ 497 uint32_t msg = (uintptr_t)arg1; 498 uint32_t arg = (uint32_t)arg2; 499 const struct priv *priv = NG_NODE_PRIVATE(node); 500 struct ngvcc *vcc; 501 u_int vci, vpi, state; 502 struct ng_mesg *mesg; 503 int error; 504 505 switch (msg) { 506 507 case ATM_MSG_FLOW_CONTROL: 508 { 509 struct ngm_queue_state *qstate; 510 511 /* find the connection */ 512 vci = arg & 0xffff; 513 vpi = (arg >> 16) & 0xff; 514 state = (arg >> 24) & 1; 515 LIST_FOREACH(vcc, &priv->vccs, link) 516 if (vcc->vci == vci && vcc->vpi == vpi) 517 break; 518 if (vcc == NULL) 519 break; 520 521 /* convert into a flow control message */ 522 NG_MKMESSAGE(mesg, NGM_FLOW_COOKIE, 523 state ? NGM_HIGH_WATER_PASSED : NGM_LOW_WATER_PASSED, 524 sizeof(struct ngm_queue_state), M_NOWAIT); 525 if (mesg == NULL) 526 break; 527 qstate = (struct ngm_queue_state *)mesg->data; 528 529 /* XXX have to figure out how to get that info */ 530 531 NG_SEND_MSG_HOOK(error, node, mesg, vcc->hook, NULL); 532 break; 533 } 534 535 case ATM_MSG_VCC_CHANGED: 536 { 537 struct ngm_atm_vcc_change *chg; 538 539 if (priv->manage == NULL) 540 break; 541 NG_MKMESSAGE(mesg, NGM_ATM_COOKIE, NGM_ATM_VCC_CHANGE, 542 sizeof(struct ngm_atm_vcc_change), M_NOWAIT); 543 if (mesg == NULL) 544 break; 545 chg = (struct ngm_atm_vcc_change *)mesg->data; 546 chg->vci = arg & 0xffff; 547 chg->vpi = (arg >> 16) & 0xff; 548 chg->state = (arg >> 24) & 1; 549 chg->node = NG_NODE_ID(node); 550 NG_SEND_MSG_HOOK(error, node, mesg, priv->manage, NULL); 551 break; 552 } 553 554 case ATM_MSG_CARRIER_CHANGE: 555 { 556 struct ngm_atm_carrier_change *chg; 557 558 if (priv->manage == NULL) 559 break; 560 NG_MKMESSAGE(mesg, NGM_ATM_COOKIE, NGM_ATM_CARRIER_CHANGE, 561 sizeof(struct ngm_atm_carrier_change), M_NOWAIT); 562 if (mesg == NULL) 563 break; 564 chg = (struct ngm_atm_carrier_change *)mesg->data; 565 chg->state = arg & 1; 566 chg->node = NG_NODE_ID(node); 567 NG_SEND_MSG_HOOK(error, node, mesg, priv->manage, NULL); 568 break; 569 } 570 } 571} 572#endif 573 574/* 575 * Use send_fn to get the right lock 576 */ 577static void 578ng_atm_message(struct ifnet *ifp, uint32_t msg, uint32_t arg) 579{ 580#ifdef notyet 581 const node_p node = IFP2NG(ifp); 582 583 (void)ng_send_fn(node, NULL, ng_atm_message_func, 584 (void *)(uintptr_t)msg, arg); 585#endif 586} 587 588/************************************************************ 589 * 590 * CPCS 591 */ 592/* 593 * Open a channel for the user 594 */ 595static int 596ng_atm_cpcs_init(node_p node, const struct ngm_atm_cpcs_init *arg) 597{ 598 struct priv *priv = NG_NODE_PRIVATE(node); 599 const struct ifatm_mib *mib; 600 struct ngvcc *vcc; 601 struct atmio_openvcc data; 602 int err; 603 604 if(priv->ifp->if_ioctl == NULL) 605 return (ENXIO); 606 607 mib = (const struct ifatm_mib *)(priv->ifp->if_linkmib); 608 609 LIST_FOREACH(vcc, &priv->vccs, link) 610 if (strcmp(arg->name, NG_HOOK_NAME(vcc->hook)) == 0) 611 break; 612 if (vcc == NULL) 613 return (ENOTCONN); 614 if (vcc->vci != 0) 615 return (EISCONN); 616 617 /* 618 * Check user arguments and construct ioctl argument 619 */ 620 memset(&data, 0, sizeof(data)); 621 622 data.rxhand = vcc; 623 624 switch (data.param.aal = arg->aal) { 625 626 case ATMIO_AAL_5: 627 case ATMIO_AAL_0: 628 case ATMIO_AAL_RAW: 629 break; 630 631 default: 632 return (EINVAL); 633 } 634 635 if (arg->vpi > 0xff) 636 return (EINVAL); 637 data.param.vpi = arg->vpi; 638 639 if (arg->vci == 0 || arg->vci > 0xffff) 640 return (EINVAL); 641 data.param.vci = arg->vci; 642 643 data.param.tparam.pcr = arg->pcr; 644 645 if (arg->mcr > arg->pcr) 646 return (EINVAL); 647 data.param.tparam.mcr = arg->mcr; 648 649 if (!(arg->flags & ATMIO_FLAG_NOTX)) { 650 if (arg->tmtu > (1 << 16) || arg->tmtu == 0) 651 return (EINVAL); 652 data.param.tmtu = arg->tmtu; 653 } 654 if (!(arg->flags & ATMIO_FLAG_NORX)) { 655 if (arg->rmtu > (1 << 16) || arg->rmtu == 0) 656 return (EINVAL); 657 data.param.rmtu = arg->rmtu; 658 } 659 660 switch (data.param.traffic = arg->traffic) { 661 662 case ATMIO_TRAFFIC_UBR: 663 case ATMIO_TRAFFIC_CBR: 664 break; 665 666 case ATMIO_TRAFFIC_VBR: 667 if (arg->scr > arg->pcr) 668 return (EINVAL); 669 data.param.tparam.scr = arg->scr; 670 671 if (arg->mbs > (1 << 24)) 672 return (EINVAL); 673 data.param.tparam.mbs = arg->mbs; 674 break; 675 676 case ATMIO_TRAFFIC_ABR: 677 if (arg->icr > arg->pcr || arg->icr < arg->mcr) 678 return (EINVAL); 679 data.param.tparam.icr = arg->icr; 680 681 if (arg->tbe == 0 || arg->tbe > (1 << 24)) 682 return (EINVAL); 683 data.param.tparam.tbe = arg->tbe; 684 685 if (arg->nrm > 0x7) 686 return (EINVAL); 687 data.param.tparam.nrm = arg->nrm; 688 689 if (arg->trm > 0x7) 690 return (EINVAL); 691 data.param.tparam.trm = arg->trm; 692 693 if (arg->adtf > 0x3ff) 694 return (EINVAL); 695 data.param.tparam.adtf = arg->adtf; 696 697 if (arg->rif > 0xf) 698 return (EINVAL); 699 data.param.tparam.rif = arg->rif; 700 701 if (arg->rdf > 0xf) 702 return (EINVAL); 703 data.param.tparam.rdf = arg->rdf; 704 705 if (arg->cdf > 0x7) 706 return (EINVAL); 707 data.param.tparam.cdf = arg->cdf; 708 709 break; 710 711 default: 712 return (EINVAL); 713 } 714 715 if ((arg->flags & ATMIO_FLAG_NORX) && (arg->flags & ATMIO_FLAG_NOTX)) 716 return (EINVAL); 717 718 data.param.flags = arg->flags & ~(ATM_PH_AAL5 | ATM_PH_LLCSNAP); 719 data.param.flags |= ATMIO_FLAG_NG; 720 721 err = (*priv->ifp->if_ioctl)(priv->ifp, SIOCATMOPENVCC, (caddr_t)&data); 722 723 if (err == 0) { 724 vcc->vci = data.param.vci; 725 vcc->vpi = data.param.vpi; 726 vcc->flags = arg->flags; 727 } 728 729 return (err); 730} 731 732/* 733 * Issue the close command to the driver 734 */ 735static int 736cpcs_term(const struct priv *priv, u_int vpi, u_int vci) 737{ 738 struct atmio_closevcc data; 739 740 if (priv->ifp->if_ioctl == NULL) 741 return ENXIO; 742 743 data.vpi = vpi; 744 data.vci = vci; 745 746 return ((*priv->ifp->if_ioctl)(priv->ifp, 747 SIOCATMCLOSEVCC, (caddr_t)&data)); 748} 749 750 751/* 752 * Close a channel by request of the user 753 */ 754static int 755ng_atm_cpcs_term(node_p node, const struct ngm_atm_cpcs_term *arg) 756{ 757 struct priv *priv = NG_NODE_PRIVATE(node); 758 struct ngvcc *vcc; 759 int error; 760 761 LIST_FOREACH(vcc, &priv->vccs, link) 762 if(strcmp(arg->name, NG_HOOK_NAME(vcc->hook)) == 0) 763 break; 764 if (vcc == NULL) 765 return (ENOTCONN); 766 if (vcc->vci == 0) 767 return (ENOTCONN); 768 769 error = cpcs_term(priv, vcc->vpi, vcc->vci); 770 771 vcc->vci = 0; 772 vcc->vpi = 0; 773 vcc->flags = 0; 774 775 return (error); 776} 777 778/************************************************************/ 779/* 780 * CONTROL MESSAGES 781 */ 782 783/* 784 * Produce a textual description of the current status 785 */ 786static int 787text_status(node_p node, char *arg, u_int len) 788{ 789 const struct priv *priv = NG_NODE_PRIVATE(node); 790 const struct ifatm_mib *mib; 791 struct sbuf sbuf; 792 u_int i; 793 794 static const struct { 795 const char *name; 796 const char *vendor; 797 } devices[] = { 798 ATM_DEVICE_NAMES 799 }; 800 801 mib = (const struct ifatm_mib *)(priv->ifp->if_linkmib); 802 803 sbuf_new(&sbuf, arg, len, SBUF_FIXEDLEN); 804 sbuf_printf(&sbuf, "interface: %s%d\n", priv->ifp->if_name, 805 priv->ifp->if_unit); 806 807 if (mib->device >= sizeof(devices) / sizeof(devices[0])) 808 sbuf_printf(&sbuf, "device=unknown\nvendor=unknown\n"); 809 else 810 sbuf_printf(&sbuf, "device=%s\nvendor=%s\n", 811 devices[mib->device].name, devices[mib->device].vendor); 812 813 for (i = 0; atmmedia[i].name; i++) 814 if(mib->media == atmmedia[i].media) { 815 sbuf_printf(&sbuf, "media=%s\n", atmmedia[i].name); 816 break; 817 } 818 if(atmmedia[i].name == NULL) 819 sbuf_printf(&sbuf, "media=unknown\n"); 820 821 sbuf_printf(&sbuf, "serial=%u esi=%6D hardware=%u software=%u\n", 822 mib->serial, mib->esi, ":", mib->hw_version, mib->sw_version); 823 sbuf_printf(&sbuf, "pcr=%u vpi_bits=%u vci_bits=%u max_vpcs=%u " 824 "max_vccs=%u\n", mib->pcr, mib->vpi_bits, mib->vci_bits, 825 mib->max_vpcs, mib->max_vccs); 826 sbuf_printf(&sbuf, "ifflags=%b\n", priv->ifp->if_flags, IFFLAGS); 827 828 sbuf_finish(&sbuf); 829 830 return (sbuf_len(&sbuf)); 831} 832 833/* 834 * Get control message 835 */ 836static int 837ng_atm_rcvmsg(node_p node, item_p item, hook_p lasthook) 838{ 839 const struct priv *priv = NG_NODE_PRIVATE(node); 840 struct ng_mesg *resp = NULL; 841 struct ng_mesg *msg; 842 struct ifatm_mib *mib = (struct ifatm_mib *)(priv->ifp->if_linkmib); 843 int error = 0; 844 845 NGI_GET_MSG(item, msg); 846 847 switch (msg->header.typecookie) { 848 849 case NGM_GENERIC_COOKIE: 850 switch (msg->header.cmd) { 851 852 case NGM_TEXT_STATUS: 853 NG_MKRESPONSE(resp, msg, NG_TEXTRESPONSE, M_NOWAIT); 854 if(resp == NULL) { 855 error = ENOMEM; 856 break; 857 } 858 859 resp->header.arglen = text_status(node, 860 (char *)resp->data, resp->header.arglen) + 1; 861 break; 862 863 default: 864 error = EINVAL; 865 break; 866 } 867 break; 868 869 case NGM_ATM_COOKIE: 870 switch (msg->header.cmd) { 871 872 case NGM_ATM_GET_IFNAME: 873 NG_MKRESPONSE(resp, msg, IFNAMSIZ + 1, M_NOWAIT); 874 if (resp == NULL) { 875 error = ENOMEM; 876 break; 877 } 878 snprintf(resp->data, IFNAMSIZ + 1, "%s%d", 879 priv->ifp->if_name, priv->ifp->if_unit); 880 break; 881 882 case NGM_ATM_GET_CONFIG: 883 { 884 struct ngm_atm_config *config; 885 886 NG_MKRESPONSE(resp, msg, sizeof(*config), M_NOWAIT); 887 if (resp == NULL) { 888 error = ENOMEM; 889 break; 890 } 891 config = (struct ngm_atm_config *)resp->data; 892 config->pcr = mib->pcr; 893 config->vpi_bits = mib->vpi_bits; 894 config->vci_bits = mib->vci_bits; 895 config->max_vpcs = mib->max_vpcs; 896 config->max_vccs = mib->max_vccs; 897 break; 898 } 899 900 case NGM_ATM_GET_VCCS: 901 { 902 struct atmio_vcctable *vccs; 903 size_t len; 904 905 if (priv->ifp->if_ioctl == NULL) { 906 error = ENXIO; 907 break; 908 } 909 error = (*priv->ifp->if_ioctl)(priv->ifp, 910 SIOCATMGETVCCS, (caddr_t)&vccs); 911 if (error) 912 break; 913 914 len = sizeof(*vccs) + 915 vccs->count * sizeof(vccs->vccs[0]); 916 NG_MKRESPONSE(resp, msg, len, M_NOWAIT); 917 if (resp == NULL) { 918 error = ENOMEM; 919 free(vccs, M_DEVBUF); 920 break; 921 } 922 923 (void)memcpy(resp->data, vccs, len); 924 free(vccs, M_DEVBUF); 925 926 break; 927 } 928 929 case NGM_ATM_GET_VCC: 930 { 931 char hook[NG_HOOKLEN + 1]; 932 struct atmio_vcctable *vccs; 933 struct ngvcc *vcc; 934 u_int i; 935 936 if (priv->ifp->if_ioctl == NULL) { 937 error = ENXIO; 938 break; 939 } 940 if (msg->header.arglen != NG_HOOKLEN + 1) { 941 error = EINVAL; 942 break; 943 } 944 strncpy(hook, msg->data, NG_HOOKLEN + 1); 945 hook[NG_HOOKLEN] = '\0'; 946 LIST_FOREACH(vcc, &priv->vccs, link) 947 if (strcmp(NG_HOOK_NAME(vcc->hook), hook) == 0) 948 break; 949 if (vcc == NULL) { 950 error = ENOTCONN; 951 break; 952 } 953 error = (*priv->ifp->if_ioctl)(priv->ifp, 954 SIOCATMGETVCCS, (caddr_t)&vccs); 955 if (error) 956 break; 957 958 for (i = 0; i < vccs->count; i++) 959 if (vccs->vccs[i].vpi == vcc->vpi && 960 vccs->vccs[i].vci == vcc->vci) 961 break; 962 if (i == vccs->count) { 963 error = ENOTCONN; 964 free(vccs, M_DEVBUF); 965 break; 966 } 967 968 NG_MKRESPONSE(resp, msg, sizeof(vccs->vccs[0]), 969 M_NOWAIT); 970 if (resp == NULL) { 971 error = ENOMEM; 972 free(vccs, M_DEVBUF); 973 break; 974 } 975 976 *(struct atmio_vcc *)resp->data = vccs->vccs[i]; 977 free(vccs, M_DEVBUF); 978 break; 979 } 980 981 case NGM_ATM_GET_VCCID: 982 { 983 struct atmio_vcc *arg; 984 struct atmio_vcctable *vccs; 985 u_int i; 986 987 if (priv->ifp->if_ioctl == NULL) { 988 error = ENXIO; 989 break; 990 } 991 if (msg->header.arglen != sizeof(*arg)) { 992 error = EINVAL; 993 break; 994 } 995 arg = (struct atmio_vcc *)msg->data; 996 997 error = (*priv->ifp->if_ioctl)(priv->ifp, 998 SIOCATMGETVCCS, (caddr_t)&vccs); 999 if (error) 1000 break; 1001 1002 for (i = 0; i < vccs->count; i++) 1003 if (vccs->vccs[i].vpi == arg->vpi && 1004 vccs->vccs[i].vci == arg->vci) 1005 break; 1006 if (i == vccs->count) { 1007 error = ENOTCONN; 1008 free(vccs, M_DEVBUF); 1009 break; 1010 } 1011 1012 NG_MKRESPONSE(resp, msg, sizeof(vccs->vccs[0]), 1013 M_NOWAIT); 1014 if (resp == NULL) { 1015 error = ENOMEM; 1016 free(vccs, M_DEVBUF); 1017 break; 1018 } 1019 1020 *(struct atmio_vcc *)resp->data = vccs->vccs[i]; 1021 free(vccs, M_DEVBUF); 1022 break; 1023 } 1024 1025 case NGM_ATM_CPCS_INIT: 1026 if (msg->header.arglen != 1027 sizeof(struct ngm_atm_cpcs_init)) { 1028 error = EINVAL; 1029 break; 1030 } 1031 error = ng_atm_cpcs_init(node, 1032 (struct ngm_atm_cpcs_init *)msg->data); 1033 break; 1034 1035 case NGM_ATM_CPCS_TERM: 1036 if (msg->header.arglen != 1037 sizeof(struct ngm_atm_cpcs_term)) { 1038 error = EINVAL; 1039 break; 1040 } 1041 error = ng_atm_cpcs_term(node, 1042 (struct ngm_atm_cpcs_term *)msg->data); 1043 break; 1044 1045 case NGM_ATM_GET_STATS: 1046 { 1047 struct ngm_atm_stats *p; 1048 1049 if (msg->header.arglen != 0) { 1050 error = EINVAL; 1051 break; 1052 } 1053 NG_MKRESPONSE(resp, msg, sizeof(*p), M_NOWAIT); 1054 if (resp == NULL) { 1055 error = ENOMEM; 1056 break; 1057 } 1058 p = (struct ngm_atm_stats *)resp->data; 1059 p->in_packets = priv->in_packets; 1060 p->out_packets = priv->out_packets; 1061 p->in_errors = priv->in_errors; 1062 p->out_errors = priv->out_errors; 1063 1064 break; 1065 } 1066 1067 default: 1068 error = EINVAL; 1069 break; 1070 } 1071 break; 1072 1073 default: 1074 error = EINVAL; 1075 break; 1076 } 1077 1078 NG_RESPOND_MSG(error, node, item, resp); 1079 NG_FREE_MSG(msg); 1080 return (error); 1081} 1082 1083/************************************************************/ 1084/* 1085 * HOOK MANAGEMENT 1086 */ 1087 1088/* 1089 * A new hook is create that will be connected to the node. 1090 * Check, whether the name is one of the predefined ones. 1091 * If not, create a new entry into the vcc list. 1092 */ 1093static int 1094ng_atm_newhook(node_p node, hook_p hook, const char *name) 1095{ 1096 struct priv *priv = NG_NODE_PRIVATE(node); 1097 struct ngvcc *vcc; 1098 1099 if (strcmp(name, "input") == 0) { 1100 priv->input = hook; 1101 NG_HOOK_SET_RCVDATA(hook, ng_atm_rcvdrop); 1102 return (0); 1103 } 1104 if (strcmp(name, "output") == 0) { 1105 priv->output = hook; 1106 NG_HOOK_SET_RCVDATA(hook, ng_atm_rcvdrop); 1107 return (0); 1108 } 1109 if (strcmp(name, "orphans") == 0) { 1110 priv->orphans = hook; 1111 NG_HOOK_SET_RCVDATA(hook, ng_atm_rcvdrop); 1112 return (0); 1113 } 1114 1115 /* 1116 * Allocate a new entry 1117 */ 1118 vcc = malloc(sizeof(*vcc), M_NETGRAPH, M_NOWAIT | M_ZERO); 1119 if (vcc == NULL) 1120 return (ENOMEM); 1121 1122 vcc->hook = hook; 1123 NG_HOOK_SET_PRIVATE(hook, vcc); 1124 1125 LIST_INSERT_HEAD(&priv->vccs, vcc, link); 1126 1127 if (strcmp(name, "manage") == 0) 1128 priv->manage = hook; 1129 1130 return (0); 1131} 1132 1133/* 1134 * Connect. Set the peer to queuing. 1135 */ 1136static int 1137ng_atm_connect(hook_p hook) 1138{ 1139 if (NG_HOOK_PRIVATE(hook) != NULL) 1140 NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 1141 1142 return (0); 1143} 1144 1145/* 1146 * Disconnect a HOOK 1147 */ 1148static int 1149ng_atm_disconnect(hook_p hook) 1150{ 1151 struct priv *priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1152 struct ngvcc *vcc = NG_HOOK_PRIVATE(hook); 1153 1154 if (vcc == NULL) { 1155 if (hook == priv->output) { 1156 priv->output = NULL; 1157 return (0); 1158 } 1159 if (hook == priv->input) { 1160 priv->input = NULL; 1161 return (0); 1162 } 1163 if (hook == priv->orphans) { 1164 priv->orphans = NULL; 1165 return (0); 1166 } 1167 log(LOG_ERR, "ng_atm: bad hook '%s'", NG_HOOK_NAME(hook)); 1168 return (0); 1169 } 1170 1171 /* don't terminate if we are detaching from the interface */ 1172 if (vcc->vci != 0 && priv->ifp != NULL) 1173 (void)cpcs_term(priv, vcc->vpi, vcc->vci); 1174 1175 NG_HOOK_SET_PRIVATE(hook, NULL); 1176 1177 LIST_REMOVE(vcc, link); 1178 free(vcc, M_NETGRAPH); 1179 1180 if (hook == priv->manage) 1181 priv->manage = NULL; 1182 1183 return (0); 1184} 1185 1186/************************************************************/ 1187/* 1188 * NODE MANAGEMENT 1189 */ 1190 1191/* 1192 * ATM interface attached - create a node and name it like the interface. 1193 */ 1194static void 1195ng_atm_attach(struct ifnet *ifp) 1196{ 1197 char name[IFNAMSIZ+1]; 1198 node_p node; 1199 struct priv *priv; 1200 1201 KASSERT(IFP2NG(ifp) == 0, ("%s: node alreay exists?", __FUNCTION__)); 1202 1203 snprintf(name, sizeof(name), "%s%d", ifp->if_name, ifp->if_unit); 1204 1205 if (ng_make_node_common(&ng_atm_typestruct, &node) != 0) { 1206 log(LOG_ERR, "%s: can't create node for %s\n", 1207 __FUNCTION__, name); 1208 return; 1209 } 1210 1211 priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 1212 if (priv == NULL) { 1213 log(LOG_ERR, "%s: can't allocate memory for %s\n", 1214 __FUNCTION__, name); 1215 NG_NODE_UNREF(node); 1216 return; 1217 } 1218 NG_NODE_SET_PRIVATE(node, priv); 1219 priv->ifp = ifp; 1220 LIST_INIT(&priv->vccs); 1221 IFP2NG(ifp) = node; 1222 1223 if (ng_name_node(node, name) != 0) { 1224 log(LOG_WARNING, "%s: can't name node %s\n", 1225 __FUNCTION__, name); 1226 } 1227} 1228 1229/* 1230 * ATM interface detached - destroy node. 1231 */ 1232static void 1233ng_atm_detach(struct ifnet *ifp) 1234{ 1235 const node_p node = IFP2NG(ifp); 1236 struct priv *priv; 1237 1238 if(node == NULL) 1239 return; 1240 1241 NG_NODE_REALLY_DIE(node); 1242 1243 priv = NG_NODE_PRIVATE(node); 1244 IFP2NG(priv->ifp) = NULL; 1245 priv->ifp = NULL; 1246 1247 ng_rmnode_self(node); 1248} 1249 1250/* 1251 * Shutdown the node. This is called from the shutdown message processing. 1252 */ 1253static int 1254ng_atm_shutdown(node_p node) 1255{ 1256 struct priv *priv = NG_NODE_PRIVATE(node); 1257 1258 if (node->nd_flags & NG_REALLY_DIE) { 1259 /* 1260 * We are called from unloading the ATM driver. Really, 1261 * really need to shutdown this node. The ifp was 1262 * already handled in the detach routine. 1263 */ 1264 NG_NODE_SET_PRIVATE(node, NULL); 1265 free(priv, M_NETGRAPH); 1266 1267 NG_NODE_UNREF(node); 1268 return (0); 1269 } 1270 1271#ifdef NGATM_DEBUG 1272 if (!allow_shutdown) 1273 node->nd_flags &= ~NG_INVALID; 1274 else { 1275 IFP2NG(priv->ifp) = NULL; 1276 NG_NODE_SET_PRIVATE(node, NULL); 1277 free(priv, M_NETGRAPH); 1278 NG_NODE_UNREF(node); 1279 } 1280#else 1281 /* 1282 * We are persistant - reinitialize 1283 */ 1284 node->nd_flags &= ~NG_INVALID; 1285#endif 1286 return (0); 1287} 1288 1289/* 1290 * Nodes are constructed only via interface attaches. 1291 */ 1292static int 1293ng_atm_constructor(node_p nodep) 1294{ 1295 return (EINVAL); 1296} 1297 1298/************************************************************/ 1299/* 1300 * INITIALISATION 1301 */ 1302/* 1303 * Loading and unloading of node type 1304 * 1305 * The assignments to the globals for the hooks should be ok without 1306 * a special hook. The use pattern is generally: check that the pointer 1307 * is not NULL, call the function. In the attach case this is no problem. 1308 * In the detach case we can detach only when no ATM node exists. That 1309 * means that there is no ATM interface anymore. So we are sure that 1310 * we are not in the code path in if_atmsubr.c. To prevent someone 1311 * from adding an interface after we have started to unload the node, we 1312 * take the iflist lock so an if_attach will be blocked until we are done. 1313 * XXX: perhaps the function pointers should be 'volatile' for this to work 1314 * properly. 1315 */ 1316static int 1317ng_atm_mod_event(module_t mod, int event, void *data) 1318{ 1319 struct ifnet *ifp; 1320 int error = 0; 1321 1322 switch (event) { 1323 1324 case MOD_LOAD: 1325 /* 1326 * Register function hooks 1327 */ 1328 if (ng_atm_attach_p != NULL) { 1329 error = EEXIST; 1330 break; 1331 } 1332 IFNET_RLOCK(); 1333 1334 ng_atm_attach_p = ng_atm_attach; 1335 ng_atm_detach_p = ng_atm_detach; 1336 ng_atm_output_p = ng_atm_output; 1337 ng_atm_input_p = ng_atm_input; 1338 ng_atm_input_orphan_p = ng_atm_input_orphans; 1339 ng_atm_message_p = ng_atm_message; 1340 1341 /* Create nodes for existing ATM interfaces */ 1342 TAILQ_FOREACH(ifp, &ifnet, if_link) { 1343 if (ifp->if_type == IFT_ATM) 1344 ng_atm_attach(ifp); 1345 } 1346 IFNET_RUNLOCK(); 1347 break; 1348 1349 case MOD_UNLOAD: 1350 IFNET_RLOCK(); 1351 1352 ng_atm_attach_p = NULL; 1353 ng_atm_detach_p = NULL; 1354 ng_atm_output_p = NULL; 1355 ng_atm_input_p = NULL; 1356 ng_atm_input_orphan_p = NULL; 1357 ng_atm_message_p = NULL; 1358 1359 TAILQ_FOREACH(ifp, &ifnet, if_link) { 1360 if (ifp->if_type == IFT_ATM) 1361 ng_atm_detach(ifp); 1362 } 1363 IFNET_RUNLOCK(); 1364 break; 1365 1366 default: 1367 error = EOPNOTSUPP; 1368 break; 1369 } 1370 return (error); 1371} 1372