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