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