1 2 /* 3 * ng_base.c 4 * 5 * Copyright (c) 1996-1999 Whistle Communications, Inc. 6 * All rights reserved. 7 * 8 * Subject to the following obligations and disclaimer of warranty, use and 9 * redistribution of this software, in source or object code forms, with or 10 * without modifications are expressly permitted by Whistle Communications; 11 * provided, however, that: 12 * 1. Any and all reproductions of the source or object code must include the 13 * copyright notice above and the following disclaimer of warranties; and 14 * 2. No rights are granted, in any manner or form, to use Whistle 15 * Communications, Inc. trademarks, including the mark "WHISTLE 16 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 17 * such appears in the above copyright notice or in the software. 18 * 19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 35 * OF SUCH DAMAGE. 36 * 37 * Authors: Julian Elischer <julian@freebsd.org> 38 * Archie Cobbs <archie@freebsd.org> 39 * 40 * $FreeBSD$ 41 * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $ 42 */ 43 44 /* 45 * This file implements the base netgraph code. 46 */ 47 48 #include <sys/param.h> 49 #include <sys/systm.h> 50 #include <sys/errno.h> 51 #include <sys/kernel.h> 52 #include <sys/malloc.h> 53 #include <sys/syslog.h> 54 #include <sys/linker.h> 55 #include <sys/queue.h> 56 #include <sys/mbuf.h> 57 #include <sys/ctype.h> 58 #include <machine/limits.h> 59 60 #include <net/netisr.h> 61 62 #include <netgraph/ng_message.h> 63 #include <netgraph/netgraph.h> 64 #include <netgraph/ng_parse.h> 65 66 MODULE_VERSION(netgraph, 1); 67 68 /* List of all nodes */ 69 static LIST_HEAD(, ng_node) nodelist; 70 71 /* List of installed types */ 72 static LIST_HEAD(, ng_type) typelist; 73 74 /* Hash releted definitions */ 75 #define ID_HASH_SIZE 32 /* most systems wont need even this many */ 76 static LIST_HEAD(, ng_node) ID_hash[ID_HASH_SIZE]; 77 /* Don't nead to initialise them because it's a LIST */ 78 79 /* Internal functions */ 80 static int ng_add_hook(node_p node, const char *name, hook_p * hookp); 81 static int ng_connect(hook_p hook1, hook_p hook2); 82 static void ng_disconnect_hook(hook_p hook); 83 static int ng_generic_msg(node_p here, struct ng_mesg *msg, 84 const char *retaddr, struct ng_mesg ** resp, 85 hook_p hook); 86 static ng_ID_t ng_decodeidname(const char *name); 87 static int ngb_mod_event(module_t mod, int event, void *data); 88 static void ngintr(void); 89 90 /* Our own netgraph malloc type */ 91 MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); 92 93 /* Set this to Debugger("X") to catch all errors as they occur */ 94 #ifndef TRAP_ERROR 95 #define TRAP_ERROR 96 #endif 97 98 static ng_ID_t nextID = 1; 99 100 #ifdef INVARIANTS 101 #define CHECK_DATA_MBUF(m) do { \ 102 struct mbuf *n; \ 103 int total; \ 104 \ 105 if (((m)->m_flags & M_PKTHDR) == 0) \ 106 panic("%s: !PKTHDR", __FUNCTION__); \ 107 for (total = 0, n = (m); n != NULL; n = n->m_next) \ 108 total += n->m_len; \ 109 if ((m)->m_pkthdr.len != total) { \ 110 panic("%s: %d != %d", \ 111 __FUNCTION__, (m)->m_pkthdr.len, total); \ 112 } \ 113 } while (0) 114 #else 115 #define CHECK_DATA_MBUF(m) 116 #endif 117 118 119 /************************************************************************ 120 Parse type definitions for generic messages 121 ************************************************************************/ 122 123 /* Handy structure parse type defining macro */ 124 #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ 125 static const struct ng_parse_struct_info \ 126 ng_ ## lo ## _type_info = NG_GENERIC_ ## up ## _INFO args; \ 127 static const struct ng_parse_type ng_generic_ ## lo ## _type = { \ 128 &ng_parse_struct_type, \ 129 &ng_ ## lo ## _type_info \ 130 } 131 132 DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); 133 DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); 134 DEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); 135 DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); 136 DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); 137 DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); 138 DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); 139 140 /* Get length of an array when the length is stored as a 32 bit 141 value immediately preceeding the array -- as with struct namelist 142 and struct typelist. */ 143 static int 144 ng_generic_list_getLength(const struct ng_parse_type *type, 145 const u_char *start, const u_char *buf) 146 { 147 return *((const u_int32_t *)(buf - 4)); 148 } 149 150 /* Get length of the array of struct linkinfo inside a struct hooklist */ 151 static int 152 ng_generic_linkinfo_getLength(const struct ng_parse_type *type, 153 const u_char *start, const u_char *buf) 154 { 155 const struct hooklist *hl = (const struct hooklist *)start; 156 157 return hl->nodeinfo.hooks; 158 } 159 160 /* Array type for a variable length array of struct namelist */ 161 static const struct ng_parse_array_info ng_nodeinfoarray_type_info = { 162 &ng_generic_nodeinfo_type, 163 &ng_generic_list_getLength 164 }; 165 static const struct ng_parse_type ng_generic_nodeinfoarray_type = { 166 &ng_parse_array_type, 167 &ng_nodeinfoarray_type_info 168 }; 169 170 /* Array type for a variable length array of struct typelist */ 171 static const struct ng_parse_array_info ng_typeinfoarray_type_info = { 172 &ng_generic_typeinfo_type, 173 &ng_generic_list_getLength 174 }; 175 static const struct ng_parse_type ng_generic_typeinfoarray_type = { 176 &ng_parse_array_type, 177 &ng_typeinfoarray_type_info 178 }; 179 180 /* Array type for array of struct linkinfo in struct hooklist */ 181 static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { 182 &ng_generic_linkinfo_type, 183 &ng_generic_linkinfo_getLength 184 }; 185 static const struct ng_parse_type ng_generic_linkinfo_array_type = { 186 &ng_parse_array_type, 187 &ng_generic_linkinfo_array_type_info 188 }; 189 190 DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_nodeinfoarray_type)); 191 DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, 192 (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); 193 DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, 194 (&ng_generic_nodeinfoarray_type)); 195 196 /* List of commands and how to convert arguments to/from ASCII */ 197 static const struct ng_cmdlist ng_generic_cmds[] = { 198 { 199 NGM_GENERIC_COOKIE, 200 NGM_SHUTDOWN, 201 "shutdown", 202 NULL, 203 NULL 204 }, 205 { 206 NGM_GENERIC_COOKIE, 207 NGM_MKPEER, 208 "mkpeer", 209 &ng_generic_mkpeer_type, 210 NULL 211 }, 212 { 213 NGM_GENERIC_COOKIE, 214 NGM_CONNECT, 215 "connect", 216 &ng_generic_connect_type, 217 NULL 218 }, 219 { 220 NGM_GENERIC_COOKIE, 221 NGM_NAME, 222 "name", 223 &ng_generic_name_type, 224 NULL 225 }, 226 { 227 NGM_GENERIC_COOKIE, 228 NGM_RMHOOK, 229 "rmhook", 230 &ng_generic_rmhook_type, 231 NULL 232 }, 233 { 234 NGM_GENERIC_COOKIE, 235 NGM_NODEINFO, 236 "nodeinfo", 237 NULL, 238 &ng_generic_nodeinfo_type 239 }, 240 { 241 NGM_GENERIC_COOKIE, 242 NGM_LISTHOOKS, 243 "listhooks", 244 NULL, 245 &ng_generic_hooklist_type 246 }, 247 { 248 NGM_GENERIC_COOKIE, 249 NGM_LISTNAMES, 250 "listnames", 251 NULL, 252 &ng_generic_listnodes_type /* same as NGM_LISTNODES */ 253 }, 254 { 255 NGM_GENERIC_COOKIE, 256 NGM_LISTNODES, 257 "listnodes", 258 NULL, 259 &ng_generic_listnodes_type 260 }, 261 { 262 NGM_GENERIC_COOKIE, 263 NGM_LISTTYPES, 264 "listtypes", 265 NULL, 266 &ng_generic_typeinfo_type 267 }, 268 { 269 NGM_GENERIC_COOKIE, 270 NGM_TEXT_CONFIG, 271 "textconfig", 272 NULL, 273 &ng_parse_string_type 274 }, 275 { 276 NGM_GENERIC_COOKIE, 277 NGM_TEXT_STATUS, 278 "textstatus", 279 NULL, 280 &ng_parse_string_type 281 }, 282 { 283 NGM_GENERIC_COOKIE, 284 NGM_ASCII2BINARY, 285 "ascii2binary", 286 &ng_parse_ng_mesg_type, 287 &ng_parse_ng_mesg_type 288 }, 289 { 290 NGM_GENERIC_COOKIE, 291 NGM_BINARY2ASCII, 292 "binary2ascii", 293 &ng_parse_ng_mesg_type, 294 &ng_parse_ng_mesg_type 295 }, 296 { 0 } 297 }; 298 299 /************************************************************************ 300 Node routines 301 ************************************************************************/ 302 303 /* 304 * Instantiate a node of the requested type 305 */ 306 int 307 ng_make_node(const char *typename, node_p *nodepp) 308 { 309 struct ng_type *type; 310 311 /* Check that the type makes sense */ 312 if (typename == NULL) { 313 TRAP_ERROR; 314 return (EINVAL); 315 } 316 317 /* Locate the node type */ 318 if ((type = ng_findtype(typename)) == NULL) { 319 char filename[NG_TYPELEN + 4]; 320 linker_file_t lf; 321 int error; 322 323 /* Not found, try to load it as a loadable module */ 324 snprintf(filename, sizeof(filename), "ng_%s", typename); 325 error = linker_load_file(filename, &lf); 326 if (error != 0) 327 return (error); 328 lf->userrefs++; /* pretend loaded by the syscall */ 329 330 /* Try again, as now the type should have linked itself in */ 331 if ((type = ng_findtype(typename)) == NULL) 332 return (ENXIO); 333 } 334 335 /* Call the constructor */ 336 if (type->constructor != NULL) 337 return ((*type->constructor)(nodepp)); 338 else 339 return (ng_make_node_common(type, nodepp)); 340 } 341 342 /* 343 * Generic node creation. Called by node constructors. 344 * The returned node has a reference count of 1. 345 */ 346 int 347 ng_make_node_common(struct ng_type *type, node_p *nodepp) 348 { 349 node_p node; 350 351 /* Require the node type to have been already installed */ 352 if (ng_findtype(type->name) == NULL) { 353 TRAP_ERROR; 354 return (EINVAL); 355 } 356 357 /* Make a node and try attach it to the type */ 358 MALLOC(node, node_p, sizeof(*node), M_NETGRAPH, M_NOWAIT | M_ZERO); 359 if (node == NULL) { 360 TRAP_ERROR; 361 return (ENOMEM); 362 } 363 node->type = type; 364 node->refs++; /* note reference */ 365 type->refs++; 366 367 /* Link us into the node linked list */ 368 LIST_INSERT_HEAD(&nodelist, node, nodes); 369 370 /* Initialize hook list for new node */ 371 LIST_INIT(&node->hooks); 372 373 /* get an ID and put us in the hash chain */ 374 node->ID = nextID++; /* 137 per second for 1 year before wrap */ 375 LIST_INSERT_HEAD(&ID_hash[node->ID % ID_HASH_SIZE], node, idnodes); 376 377 /* Done */ 378 *nodepp = node; 379 return (0); 380 } 381 382 /* 383 * Forceably start the shutdown process on a node. Either call 384 * it's shutdown method, or do the default shutdown if there is 385 * no type-specific method. 386 * 387 * Persistent nodes must have a type-specific method which 388 * resets the NG_INVALID flag. 389 */ 390 void 391 ng_rmnode(node_p node) 392 { 393 /* Check if it's already shutting down */ 394 if ((node->flags & NG_INVALID) != 0) 395 return; 396 397 /* Add an extra reference so it doesn't go away during this */ 398 node->refs++; 399 400 /* Mark it invalid so any newcomers know not to try use it */ 401 node->flags |= NG_INVALID; 402 403 /* Ask the type if it has anything to do in this case */ 404 if (node->type && node->type->shutdown) 405 (*node->type->shutdown)(node); 406 else { /* do the default thing */ 407 ng_unname(node); 408 ng_cutlinks(node); 409 ng_unref(node); 410 } 411 412 /* Remove extra reference, possibly the last */ 413 ng_unref(node); 414 } 415 416 /* 417 * Called by the destructor to remove any STANDARD external references 418 */ 419 void 420 ng_cutlinks(node_p node) 421 { 422 hook_p hook; 423 424 /* Make sure that this is set to stop infinite loops */ 425 node->flags |= NG_INVALID; 426 427 /* If we have sleepers, wake them up; they'll see NG_INVALID */ 428 if (node->sleepers) 429 wakeup(node); 430 431 /* Notify all remaining connected nodes to disconnect */ 432 while ((hook = LIST_FIRST(&node->hooks)) != NULL) 433 ng_destroy_hook(hook); 434 } 435 436 /* 437 * Remove a reference to the node, possibly the last 438 */ 439 void 440 ng_unref(node_p node) 441 { 442 if (--node->refs <= 0) { 443 node->type->refs--; 444 LIST_REMOVE(node, nodes); 445 LIST_REMOVE(node, idnodes); 446 FREE(node, M_NETGRAPH); 447 } 448 } 449 450 /* 451 * Wait for a node to come ready. Returns a node with a reference count; 452 * don't forget to drop it when we are done with it using ng_release_node(). 453 */ 454 int 455 ng_wait_node(node_p node, char *msg) 456 { 457 int s, error = 0; 458 459 if (msg == NULL) 460 msg = "netgraph"; 461 s = splnet(); 462 node->sleepers++; 463 node->refs++; /* the sleeping process counts as a reference */ 464 while ((node->flags & (NG_BUSY | NG_INVALID)) == NG_BUSY) 465 error = tsleep(node, (PZERO + 1) | PCATCH, msg, 0); 466 node->sleepers--; 467 if (node->flags & NG_INVALID) { 468 TRAP_ERROR; 469 error = ENXIO; 470 } else { 471 KASSERT(node->refs > 1, 472 ("%s: refs=%d", __FUNCTION__, node->refs)); 473 node->flags |= NG_BUSY; 474 } 475 splx(s); 476 477 /* Release the reference we had on it */ 478 if (error != 0) 479 ng_unref(node); 480 return error; 481 } 482 483 /* 484 * Release a node acquired via ng_wait_node() 485 */ 486 void 487 ng_release_node(node_p node) 488 { 489 /* Declare that we don't want it */ 490 node->flags &= ~NG_BUSY; 491 492 /* If we have sleepers, then wake them up */ 493 if (node->sleepers) 494 wakeup(node); 495 496 /* We also have a reference.. drop it too */ 497 ng_unref(node); 498 } 499 500 /************************************************************************ 501 Node ID handling 502 ************************************************************************/ 503 static node_p 504 ng_ID2node(ng_ID_t ID) 505 { 506 node_p np; 507 LIST_FOREACH(np, &ID_hash[ID % ID_HASH_SIZE], idnodes) { 508 if (np->ID == ID) 509 break; 510 } 511 return(np); 512 } 513 514 ng_ID_t 515 ng_node2ID(node_p node) 516 { 517 return (node->ID); 518 } 519 520 /************************************************************************ 521 Node name handling 522 ************************************************************************/ 523 524 /* 525 * Assign a node a name. Once assigned, the name cannot be changed. 526 */ 527 int 528 ng_name_node(node_p node, const char *name) 529 { 530 int i; 531 532 /* Check the name is valid */ 533 for (i = 0; i < NG_NODELEN + 1; i++) { 534 if (name[i] == '\0' || name[i] == '.' || name[i] == ':') 535 break; 536 } 537 if (i == 0 || name[i] != '\0') { 538 TRAP_ERROR; 539 return (EINVAL); 540 } 541 if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ 542 TRAP_ERROR; 543 return (EINVAL); 544 } 545 546 /* Check the node isn't already named */ 547 if (node->name != NULL) { 548 TRAP_ERROR; 549 return (EISCONN); 550 } 551 552 /* Check the name isn't already being used */ 553 if (ng_findname(node, name) != NULL) { 554 TRAP_ERROR; 555 return (EADDRINUSE); 556 } 557 558 /* Allocate space and copy it */ 559 MALLOC(node->name, char *, strlen(name) + 1, M_NETGRAPH, M_NOWAIT); 560 if (node->name == NULL) { 561 TRAP_ERROR; 562 return (ENOMEM); 563 } 564 strcpy(node->name, name); 565 566 /* The name counts as a reference */ 567 node->refs++; 568 return (0); 569 } 570 571 /* 572 * Find a node by absolute name. The name should NOT end with ':' 573 * The name "." means "this node" and "[xxx]" means "the node 574 * with ID (ie, at address) xxx". 575 * 576 * Returns the node if found, else NULL. 577 */ 578 node_p 579 ng_findname(node_p this, const char *name) 580 { 581 node_p node; 582 ng_ID_t temp; 583 584 /* "." means "this node" */ 585 if (strcmp(name, ".") == 0) 586 return(this); 587 588 /* Check for name-by-ID */ 589 if ((temp = ng_decodeidname(name)) != 0) { 590 return (ng_ID2node(temp)); 591 } 592 593 /* Find node by name */ 594 LIST_FOREACH(node, &nodelist, nodes) { 595 if (node->name != NULL && strcmp(node->name, name) == 0) 596 break; 597 } 598 return (node); 599 } 600 601 /* 602 * Decode a ID name, eg. "[f03034de]". Returns 0 if the 603 * string is not valid, otherwise returns the value. 604 */ 605 static ng_ID_t 606 ng_decodeidname(const char *name) 607 { 608 const int len = strlen(name); 609 char *eptr; 610 u_long val; 611 612 /* Check for proper length, brackets, no leading junk */ 613 if (len < 3 || name[0] != '[' || name[len - 1] != ']' 614 || !isxdigit(name[1])) 615 return (0); 616 617 /* Decode number */ 618 val = strtoul(name + 1, &eptr, 16); 619 if (eptr - name != len - 1 || val == ULONG_MAX || val == 0) 620 return ((ng_ID_t)0); 621 return (ng_ID_t)val; 622 } 623 624 /* 625 * Remove a name from a node. This should only be called 626 * when shutting down and removing the node. 627 */ 628 void 629 ng_unname(node_p node) 630 { 631 if (node->name) { 632 FREE(node->name, M_NETGRAPH); 633 node->name = NULL; 634 ng_unref(node); 635 } 636 } 637 638 /************************************************************************ 639 Hook routines 640 641 Names are not optional. Hooks are always connected, except for a 642 brief moment within these routines. 643 644 ************************************************************************/ 645 646 /* 647 * Remove a hook reference 648 */ 649 static void 650 ng_unref_hook(hook_p hook) 651 { 652 if (--hook->refs == 0) 653 FREE(hook, M_NETGRAPH); 654 } 655 656 /* 657 * Add an unconnected hook to a node. Only used internally. 658 */ 659 static int 660 ng_add_hook(node_p node, const char *name, hook_p *hookp) 661 { 662 hook_p hook; 663 int error = 0; 664 665 /* Check that the given name is good */ 666 if (name == NULL) { 667 TRAP_ERROR; 668 return (EINVAL); 669 } 670 if (ng_findhook(node, name) != NULL) { 671 TRAP_ERROR; 672 return (EEXIST); 673 } 674 675 /* Allocate the hook and link it up */ 676 MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH, M_NOWAIT | M_ZERO); 677 if (hook == NULL) { 678 TRAP_ERROR; 679 return (ENOMEM); 680 } 681 hook->refs = 1; 682 hook->flags = HK_INVALID; 683 hook->node = node; 684 node->refs++; /* each hook counts as a reference */ 685 686 /* Check if the node type code has something to say about it */ 687 if (node->type->newhook != NULL) 688 if ((error = (*node->type->newhook)(node, hook, name)) != 0) 689 goto fail; 690 691 /* 692 * The 'type' agrees so far, so go ahead and link it in. 693 * We'll ask again later when we actually connect the hooks. 694 */ 695 LIST_INSERT_HEAD(&node->hooks, hook, hooks); 696 node->numhooks++; 697 698 /* Set hook name */ 699 MALLOC(hook->name, char *, strlen(name) + 1, M_NETGRAPH, M_NOWAIT); 700 if (hook->name == NULL) { 701 error = ENOMEM; 702 LIST_REMOVE(hook, hooks); 703 node->numhooks--; 704 fail: 705 hook->node = NULL; 706 ng_unref(node); 707 ng_unref_hook(hook); /* this frees the hook */ 708 return (error); 709 } 710 strcpy(hook->name, name); 711 if (hookp) 712 *hookp = hook; 713 return (error); 714 } 715 716 /* 717 * Connect a pair of hooks. Only used internally. 718 */ 719 static int 720 ng_connect(hook_p hook1, hook_p hook2) 721 { 722 int error; 723 724 hook1->peer = hook2; 725 hook2->peer = hook1; 726 727 /* Give each node the opportunity to veto the impending connection */ 728 if (hook1->node->type->connect) { 729 if ((error = (*hook1->node->type->connect) (hook1))) { 730 ng_destroy_hook(hook1); /* also zaps hook2 */ 731 return (error); 732 } 733 } 734 if (hook2->node->type->connect) { 735 if ((error = (*hook2->node->type->connect) (hook2))) { 736 ng_destroy_hook(hook2); /* also zaps hook1 */ 737 return (error); 738 } 739 } 740 hook1->flags &= ~HK_INVALID; 741 hook2->flags &= ~HK_INVALID; 742 return (0); 743 } 744 745 /* 746 * Find a hook 747 * 748 * Node types may supply their own optimized routines for finding 749 * hooks. If none is supplied, we just do a linear search. 750 */ 751 hook_p 752 ng_findhook(node_p node, const char *name) 753 { 754 hook_p hook; 755 756 if (node->type->findhook != NULL) 757 return (*node->type->findhook)(node, name); 758 LIST_FOREACH(hook, &node->hooks, hooks) { 759 if (hook->name != NULL && strcmp(hook->name, name) == 0) 760 return (hook); 761 } 762 return (NULL); 763 } 764 765 /* 766 * Destroy a hook 767 * 768 * As hooks are always attached, this really destroys two hooks. 769 * The one given, and the one attached to it. Disconnect the hooks 770 * from each other first. 771 */ 772 void 773 ng_destroy_hook(hook_p hook) 774 { 775 hook_p peer = hook->peer; 776 777 hook->flags |= HK_INVALID; /* as soon as possible */ 778 if (peer) { 779 peer->flags |= HK_INVALID; /* as soon as possible */ 780 hook->peer = NULL; 781 peer->peer = NULL; 782 ng_disconnect_hook(peer); 783 } 784 ng_disconnect_hook(hook); 785 } 786 787 /* 788 * Notify the node of the hook's demise. This may result in more actions 789 * (e.g. shutdown) but we don't do that ourselves and don't know what 790 * happens there. If there is no appropriate handler, then just remove it 791 * (and decrement the reference count of it's node which in turn might 792 * make something happen). 793 */ 794 static void 795 ng_disconnect_hook(hook_p hook) 796 { 797 node_p node = hook->node; 798 799 /* 800 * Remove the hook from the node's list to avoid possible recursion 801 * in case the disconnection results in node shutdown. 802 */ 803 LIST_REMOVE(hook, hooks); 804 node->numhooks--; 805 if (node->type->disconnect) { 806 /* 807 * The type handler may elect to destroy the peer so don't 808 * trust its existance after this point. 809 */ 810 (*node->type->disconnect) (hook); 811 } 812 ng_unref(node); /* might be the last reference */ 813 if (hook->name) 814 FREE(hook->name, M_NETGRAPH); 815 hook->node = NULL; /* may still be referenced elsewhere */ 816 ng_unref_hook(hook); 817 } 818 819 /* 820 * Take two hooks on a node and merge the connection so that the given node 821 * is effectively bypassed. 822 */ 823 int 824 ng_bypass(hook_p hook1, hook_p hook2) 825 { 826 if (hook1->node != hook2->node) 827 return (EINVAL); 828 hook1->peer->peer = hook2->peer; 829 hook2->peer->peer = hook1->peer; 830 831 /* XXX If we ever cache methods on hooks update them as well */ 832 hook1->peer = NULL; 833 hook2->peer = NULL; 834 ng_destroy_hook(hook1); 835 ng_destroy_hook(hook2); 836 return (0); 837 } 838 839 /* 840 * Install a new netgraph type 841 */ 842 int 843 ng_newtype(struct ng_type *tp) 844 { 845 const size_t namelen = strlen(tp->name); 846 847 /* Check version and type name fields */ 848 if (tp->version != NG_VERSION || namelen == 0 || namelen > NG_TYPELEN) { 849 TRAP_ERROR; 850 return (EINVAL); 851 } 852 853 /* Check for name collision */ 854 if (ng_findtype(tp->name) != NULL) { 855 TRAP_ERROR; 856 return (EEXIST); 857 } 858 859 /* Link in new type */ 860 LIST_INSERT_HEAD(&typelist, tp, types); 861 tp->refs = 0; 862 return (0); 863 } 864 865 /* 866 * Look for a type of the name given 867 */ 868 struct ng_type * 869 ng_findtype(const char *typename) 870 { 871 struct ng_type *type; 872 873 LIST_FOREACH(type, &typelist, types) { 874 if (strcmp(type->name, typename) == 0) 875 break; 876 } 877 return (type); 878 } 879 880 881 /************************************************************************ 882 Composite routines 883 ************************************************************************/ 884 885 /* 886 * Make a peer and connect. The order is arranged to minimise 887 * the work needed to back out in case of error. 888 */ 889 int 890 ng_mkpeer(node_p node, const char *name, const char *name2, char *type) 891 { 892 node_p node2; 893 hook_p hook; 894 hook_p hook2; 895 int error; 896 897 if ((error = ng_add_hook(node, name, &hook))) 898 return (error); 899 if ((error = ng_make_node(type, &node2))) { 900 ng_destroy_hook(hook); 901 return (error); 902 } 903 if ((error = ng_add_hook(node2, name2, &hook2))) { 904 ng_rmnode(node2); 905 ng_destroy_hook(hook); 906 return (error); 907 } 908 909 /* 910 * Actually link the two hooks together.. on failure they are 911 * destroyed so we don't have to do that here. 912 */ 913 if ((error = ng_connect(hook, hook2))) 914 ng_rmnode(node2); 915 return (error); 916 } 917 918 /* 919 * Connect two nodes using the specified hooks 920 */ 921 int 922 ng_con_nodes(node_p node, const char *name, node_p node2, const char *name2) 923 { 924 int error; 925 hook_p hook; 926 hook_p hook2; 927 928 if ((error = ng_add_hook(node, name, &hook))) 929 return (error); 930 if ((error = ng_add_hook(node2, name2, &hook2))) { 931 ng_destroy_hook(hook); 932 return (error); 933 } 934 return (ng_connect(hook, hook2)); 935 } 936 937 /* 938 * Parse and verify a string of the form: <NODE:><PATH> 939 * 940 * Such a string can refer to a specific node or a specific hook 941 * on a specific node, depending on how you look at it. In the 942 * latter case, the PATH component must not end in a dot. 943 * 944 * Both <NODE:> and <PATH> are optional. The <PATH> is a string 945 * of hook names separated by dots. This breaks out the original 946 * string, setting *nodep to "NODE" (or NULL if none) and *pathp 947 * to "PATH" (or NULL if degenerate). Also, *hookp will point to 948 * the final hook component of <PATH>, if any, otherwise NULL. 949 * 950 * This returns -1 if the path is malformed. The char ** are optional. 951 */ 952 953 int 954 ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 955 { 956 char *node, *path, *hook; 957 int k; 958 959 /* 960 * Extract absolute NODE, if any 961 */ 962 for (path = addr; *path && *path != ':'; path++); 963 if (*path) { 964 node = addr; /* Here's the NODE */ 965 *path++ = '\0'; /* Here's the PATH */ 966 967 /* Node name must not be empty */ 968 if (!*node) 969 return -1; 970 971 /* A name of "." is OK; otherwise '.' not allowed */ 972 if (strcmp(node, ".") != 0) { 973 for (k = 0; node[k]; k++) 974 if (node[k] == '.') 975 return -1; 976 } 977 } else { 978 node = NULL; /* No absolute NODE */ 979 path = addr; /* Here's the PATH */ 980 } 981 982 /* Snoop for illegal characters in PATH */ 983 for (k = 0; path[k]; k++) 984 if (path[k] == ':') 985 return -1; 986 987 /* Check for no repeated dots in PATH */ 988 for (k = 0; path[k]; k++) 989 if (path[k] == '.' && path[k + 1] == '.') 990 return -1; 991 992 /* Remove extra (degenerate) dots from beginning or end of PATH */ 993 if (path[0] == '.') 994 path++; 995 if (*path && path[strlen(path) - 1] == '.') 996 path[strlen(path) - 1] = 0; 997 998 /* If PATH has a dot, then we're not talking about a hook */ 999 if (*path) { 1000 for (hook = path, k = 0; path[k]; k++) 1001 if (path[k] == '.') { 1002 hook = NULL; 1003 break; 1004 } 1005 } else 1006 path = hook = NULL; 1007 1008 /* Done */ 1009 if (nodep) 1010 *nodep = node; 1011 if (pathp) 1012 *pathp = path; 1013 if (hookp) 1014 *hookp = hook; 1015 return (0); 1016 } 1017 1018 /* 1019 * Given a path, which may be absolute or relative, and a starting node, 1020 * return the destination node. Compute the "return address" if desired. 1021 */ 1022 int 1023 ng_path2node(node_p here, const char *address, node_p *destp, char **rtnp, 1024 hook_p *lasthook) 1025 { 1026 const node_p start = here; 1027 char fullpath[NG_PATHLEN + 1]; 1028 char *nodename, *path, pbuf[2]; 1029 node_p node; 1030 char *cp; 1031 hook_p hook = NULL; 1032 1033 /* Initialize */ 1034 if (rtnp) 1035 *rtnp = NULL; 1036 if (destp == NULL) 1037 return EINVAL; 1038 *destp = NULL; 1039 1040 /* Make a writable copy of address for ng_path_parse() */ 1041 strncpy(fullpath, address, sizeof(fullpath) - 1); 1042 fullpath[sizeof(fullpath) - 1] = '\0'; 1043 1044 /* Parse out node and sequence of hooks */ 1045 if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 1046 TRAP_ERROR; 1047 return EINVAL; 1048 } 1049 if (path == NULL) { 1050 pbuf[0] = '.'; /* Needs to be writable */ 1051 pbuf[1] = '\0'; 1052 path = pbuf; 1053 } 1054 1055 /* For an absolute address, jump to the starting node */ 1056 if (nodename) { 1057 node = ng_findname(here, nodename); 1058 if (node == NULL) { 1059 TRAP_ERROR; 1060 return (ENOENT); 1061 } 1062 } else 1063 node = here; 1064 1065 /* Now follow the sequence of hooks */ 1066 for (cp = path; node != NULL && *cp != '\0'; ) { 1067 char *segment; 1068 1069 /* 1070 * Break out the next path segment. Replace the dot we just 1071 * found with a NUL; "cp" points to the next segment (or the 1072 * NUL at the end). 1073 */ 1074 for (segment = cp; *cp != '\0'; cp++) { 1075 if (*cp == '.') { 1076 *cp++ = '\0'; 1077 break; 1078 } 1079 } 1080 1081 /* Empty segment */ 1082 if (*segment == '\0') 1083 continue; 1084 1085 /* We have a segment, so look for a hook by that name */ 1086 hook = ng_findhook(node, segment); 1087 1088 /* Can't get there from here... */ 1089 if (hook == NULL 1090 || hook->peer == NULL 1091 || (hook->flags & HK_INVALID) != 0) { 1092 TRAP_ERROR; 1093 return (ENOENT); 1094 } 1095 1096 /* Hop on over to the next node */ 1097 node = hook->peer->node; 1098 } 1099 1100 /* If node somehow missing, fail here (probably this is not needed) */ 1101 if (node == NULL) { 1102 TRAP_ERROR; 1103 return (ENXIO); 1104 } 1105 1106 /* Now compute return address, i.e., the path to the sender */ 1107 if (rtnp != NULL) { 1108 MALLOC(*rtnp, char *, NG_NODELEN + 2, M_NETGRAPH, M_NOWAIT); 1109 if (*rtnp == NULL) { 1110 TRAP_ERROR; 1111 return (ENOMEM); 1112 } 1113 if (start->name != NULL) 1114 sprintf(*rtnp, "%s:", start->name); 1115 else 1116 sprintf(*rtnp, "[%x]:", ng_node2ID(start)); 1117 } 1118 1119 /* Done */ 1120 *destp = node; 1121 if (lasthook != NULL) 1122 *lasthook = hook ? hook->peer : NULL; 1123 return (0); 1124 } 1125 1126 /* 1127 * Call the appropriate message handler for the object. 1128 * It is up to the message handler to free the message. 1129 * If it's a generic message, handle it generically, otherwise 1130 * call the type's message handler (if it exists) 1131 */ 1132 1133 #define CALL_MSG_HANDLER(error, node, msg, retaddr, resp, hook) \ 1134 do { \ 1135 if((msg)->header.typecookie == NGM_GENERIC_COOKIE) { \ 1136 (error) = ng_generic_msg((node), (msg), \ 1137 (retaddr), (resp), (hook)); \ 1138 } else { \ 1139 if ((node)->type->rcvmsg != NULL) { \ 1140 (error) = (*(node)->type->rcvmsg)((node), \ 1141 (msg), (retaddr), (resp), (hook)); \ 1142 } else { \ 1143 TRAP_ERROR; \ 1144 FREE((msg), M_NETGRAPH); \ 1145 (error) = EINVAL; \ 1146 } \ 1147 } \ 1148 } while (0) 1149 1150 1151 /* 1152 * Send a control message to a node 1153 */ 1154 int 1155 ng_send_msg(node_p here, struct ng_mesg *msg, const char *address, 1156 struct ng_mesg **rptr) 1157 { 1158 node_p dest = NULL; 1159 char *retaddr = NULL; 1160 int error; 1161 hook_p lasthook; 1162 1163 /* Find the target node */ 1164 error = ng_path2node(here, address, &dest, &retaddr, &lasthook); 1165 if (error) { 1166 FREE(msg, M_NETGRAPH); 1167 return error; 1168 } 1169 1170 /* Make sure the resp field is null before we start */ 1171 if (rptr != NULL) 1172 *rptr = NULL; 1173 1174 CALL_MSG_HANDLER(error, dest, msg, retaddr, rptr, lasthook); 1175 1176 /* Make sure that if there is a response, it has the RESP bit set */ 1177 if ((error == 0) && rptr && *rptr) 1178 (*rptr)->header.flags |= NGF_RESP; 1179 1180 /* 1181 * If we had a return address it is up to us to free it. They should 1182 * have taken a copy if they needed to make a delayed response. 1183 */ 1184 if (retaddr) 1185 FREE(retaddr, M_NETGRAPH); 1186 return (error); 1187 } 1188 1189 /* 1190 * Implement the 'generic' control messages 1191 */ 1192 static int 1193 ng_generic_msg(node_p here, struct ng_mesg *msg, const char *retaddr, 1194 struct ng_mesg **resp, hook_p lasthook) 1195 { 1196 int error = 0; 1197 1198 if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 1199 TRAP_ERROR; 1200 FREE(msg, M_NETGRAPH); 1201 return (EINVAL); 1202 } 1203 switch (msg->header.cmd) { 1204 case NGM_SHUTDOWN: 1205 ng_rmnode(here); 1206 break; 1207 case NGM_MKPEER: 1208 { 1209 struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 1210 1211 if (msg->header.arglen != sizeof(*mkp)) { 1212 TRAP_ERROR; 1213 return (EINVAL); 1214 } 1215 mkp->type[sizeof(mkp->type) - 1] = '\0'; 1216 mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 1217 mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 1218 error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 1219 break; 1220 } 1221 case NGM_CONNECT: 1222 { 1223 struct ngm_connect *const con = 1224 (struct ngm_connect *) msg->data; 1225 node_p node2; 1226 1227 if (msg->header.arglen != sizeof(*con)) { 1228 TRAP_ERROR; 1229 return (EINVAL); 1230 } 1231 con->path[sizeof(con->path) - 1] = '\0'; 1232 con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 1233 con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 1234 error = ng_path2node(here, con->path, &node2, NULL, NULL); 1235 if (error) 1236 break; 1237 error = ng_con_nodes(here, con->ourhook, node2, con->peerhook); 1238 break; 1239 } 1240 case NGM_NAME: 1241 { 1242 struct ngm_name *const nam = (struct ngm_name *) msg->data; 1243 1244 if (msg->header.arglen != sizeof(*nam)) { 1245 TRAP_ERROR; 1246 return (EINVAL); 1247 } 1248 nam->name[sizeof(nam->name) - 1] = '\0'; 1249 error = ng_name_node(here, nam->name); 1250 break; 1251 } 1252 case NGM_RMHOOK: 1253 { 1254 struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 1255 hook_p hook; 1256 1257 if (msg->header.arglen != sizeof(*rmh)) { 1258 TRAP_ERROR; 1259 return (EINVAL); 1260 } 1261 rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 1262 if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 1263 ng_destroy_hook(hook); 1264 break; 1265 } 1266 case NGM_NODEINFO: 1267 { 1268 struct nodeinfo *ni; 1269 struct ng_mesg *rp; 1270 1271 /* Get response struct */ 1272 if (resp == NULL) { 1273 error = EINVAL; 1274 break; 1275 } 1276 NG_MKRESPONSE(rp, msg, sizeof(*ni), M_NOWAIT); 1277 if (rp == NULL) { 1278 error = ENOMEM; 1279 break; 1280 } 1281 1282 /* Fill in node info */ 1283 ni = (struct nodeinfo *) rp->data; 1284 if (here->name != NULL) 1285 strncpy(ni->name, here->name, NG_NODELEN); 1286 strncpy(ni->type, here->type->name, NG_TYPELEN); 1287 ni->id = ng_node2ID(here); 1288 ni->hooks = here->numhooks; 1289 *resp = rp; 1290 break; 1291 } 1292 case NGM_LISTHOOKS: 1293 { 1294 const int nhooks = here->numhooks; 1295 struct hooklist *hl; 1296 struct nodeinfo *ni; 1297 struct ng_mesg *rp; 1298 hook_p hook; 1299 1300 /* Get response struct */ 1301 if (resp == NULL) { 1302 error = EINVAL; 1303 break; 1304 } 1305 NG_MKRESPONSE(rp, msg, sizeof(*hl) 1306 + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 1307 if (rp == NULL) { 1308 error = ENOMEM; 1309 break; 1310 } 1311 hl = (struct hooklist *) rp->data; 1312 ni = &hl->nodeinfo; 1313 1314 /* Fill in node info */ 1315 if (here->name) 1316 strncpy(ni->name, here->name, NG_NODELEN); 1317 strncpy(ni->type, here->type->name, NG_TYPELEN); 1318 ni->id = ng_node2ID(here); 1319 1320 /* Cycle through the linked list of hooks */ 1321 ni->hooks = 0; 1322 LIST_FOREACH(hook, &here->hooks, hooks) { 1323 struct linkinfo *const link = &hl->link[ni->hooks]; 1324 1325 if (ni->hooks >= nhooks) { 1326 log(LOG_ERR, "%s: number of %s changed\n", 1327 __FUNCTION__, "hooks"); 1328 break; 1329 } 1330 if ((hook->flags & HK_INVALID) != 0) 1331 continue; 1332 strncpy(link->ourhook, hook->name, NG_HOOKLEN); 1333 strncpy(link->peerhook, hook->peer->name, NG_HOOKLEN); 1334 if (hook->peer->node->name != NULL) 1335 strncpy(link->nodeinfo.name, 1336 hook->peer->node->name, NG_NODELEN); 1337 strncpy(link->nodeinfo.type, 1338 hook->peer->node->type->name, NG_TYPELEN); 1339 link->nodeinfo.id = ng_node2ID(hook->peer->node); 1340 link->nodeinfo.hooks = hook->peer->node->numhooks; 1341 ni->hooks++; 1342 } 1343 *resp = rp; 1344 break; 1345 } 1346 1347 case NGM_LISTNAMES: 1348 case NGM_LISTNODES: 1349 { 1350 const int unnamed = (msg->header.cmd == NGM_LISTNODES); 1351 struct namelist *nl; 1352 struct ng_mesg *rp; 1353 node_p node; 1354 int num = 0; 1355 1356 if (resp == NULL) { 1357 error = EINVAL; 1358 break; 1359 } 1360 1361 /* Count number of nodes */ 1362 LIST_FOREACH(node, &nodelist, nodes) { 1363 if (unnamed || node->name != NULL) 1364 num++; 1365 } 1366 1367 /* Get response struct */ 1368 if (resp == NULL) { 1369 error = EINVAL; 1370 break; 1371 } 1372 NG_MKRESPONSE(rp, msg, sizeof(*nl) 1373 + (num * sizeof(struct nodeinfo)), M_NOWAIT); 1374 if (rp == NULL) { 1375 error = ENOMEM; 1376 break; 1377 } 1378 nl = (struct namelist *) rp->data; 1379 1380 /* Cycle through the linked list of nodes */ 1381 nl->numnames = 0; 1382 LIST_FOREACH(node, &nodelist, nodes) { 1383 struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; 1384 1385 if (nl->numnames >= num) { 1386 log(LOG_ERR, "%s: number of %s changed\n", 1387 __FUNCTION__, "nodes"); 1388 break; 1389 } 1390 if ((node->flags & NG_INVALID) != 0) 1391 continue; 1392 if (!unnamed && node->name == NULL) 1393 continue; 1394 if (node->name != NULL) 1395 strncpy(np->name, node->name, NG_NODELEN); 1396 strncpy(np->type, node->type->name, NG_TYPELEN); 1397 np->id = ng_node2ID(node); 1398 np->hooks = node->numhooks; 1399 nl->numnames++; 1400 } 1401 *resp = rp; 1402 break; 1403 } 1404 1405 case NGM_LISTTYPES: 1406 { 1407 struct typelist *tl; 1408 struct ng_mesg *rp; 1409 struct ng_type *type; 1410 int num = 0; 1411 1412 if (resp == NULL) { 1413 error = EINVAL; 1414 break; 1415 } 1416 1417 /* Count number of types */ 1418 LIST_FOREACH(type, &typelist, types) 1419 num++; 1420 1421 /* Get response struct */ 1422 if (resp == NULL) { 1423 error = EINVAL; 1424 break; 1425 } 1426 NG_MKRESPONSE(rp, msg, sizeof(*tl) 1427 + (num * sizeof(struct typeinfo)), M_NOWAIT); 1428 if (rp == NULL) { 1429 error = ENOMEM; 1430 break; 1431 } 1432 tl = (struct typelist *) rp->data; 1433 1434 /* Cycle through the linked list of types */ 1435 tl->numtypes = 0; 1436 LIST_FOREACH(type, &typelist, types) { 1437 struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 1438 1439 if (tl->numtypes >= num) { 1440 log(LOG_ERR, "%s: number of %s changed\n", 1441 __FUNCTION__, "types"); 1442 break; 1443 } 1444 strncpy(tp->type_name, type->name, NG_TYPELEN); 1445 tp->numnodes = type->refs; 1446 tl->numtypes++; 1447 } 1448 *resp = rp; 1449 break; 1450 } 1451 1452 case NGM_BINARY2ASCII: 1453 { 1454 int bufSize = 20 * 1024; /* XXX hard coded constant */ 1455 const struct ng_parse_type *argstype; 1456 const struct ng_cmdlist *c; 1457 struct ng_mesg *rp, *binary, *ascii; 1458 1459 /* Data area must contain a valid netgraph message */ 1460 binary = (struct ng_mesg *)msg->data; 1461 if (msg->header.arglen < sizeof(struct ng_mesg) 1462 || msg->header.arglen - sizeof(struct ng_mesg) 1463 < binary->header.arglen) { 1464 error = EINVAL; 1465 break; 1466 } 1467 1468 /* Get a response message with lots of room */ 1469 NG_MKRESPONSE(rp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 1470 if (rp == NULL) { 1471 error = ENOMEM; 1472 break; 1473 } 1474 ascii = (struct ng_mesg *)rp->data; 1475 1476 /* Copy binary message header to response message payload */ 1477 bcopy(binary, ascii, sizeof(*binary)); 1478 1479 /* Find command by matching typecookie and command number */ 1480 for (c = here->type->cmdlist; 1481 c != NULL && c->name != NULL; c++) { 1482 if (binary->header.typecookie == c->cookie 1483 && binary->header.cmd == c->cmd) 1484 break; 1485 } 1486 if (c == NULL || c->name == NULL) { 1487 for (c = ng_generic_cmds; c->name != NULL; c++) { 1488 if (binary->header.typecookie == c->cookie 1489 && binary->header.cmd == c->cmd) 1490 break; 1491 } 1492 if (c->name == NULL) { 1493 FREE(rp, M_NETGRAPH); 1494 error = ENOSYS; 1495 break; 1496 } 1497 } 1498 1499 /* Convert command name to ASCII */ 1500 snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 1501 "%s", c->name); 1502 1503 /* Convert command arguments to ASCII */ 1504 argstype = (binary->header.flags & NGF_RESP) ? 1505 c->respType : c->mesgType; 1506 if (argstype == NULL) 1507 *ascii->data = '\0'; 1508 else { 1509 if ((error = ng_unparse(argstype, 1510 (u_char *)binary->data, 1511 ascii->data, bufSize)) != 0) { 1512 FREE(rp, M_NETGRAPH); 1513 break; 1514 } 1515 } 1516 1517 /* Return the result as struct ng_mesg plus ASCII string */ 1518 bufSize = strlen(ascii->data) + 1; 1519 ascii->header.arglen = bufSize; 1520 rp->header.arglen = sizeof(*ascii) + bufSize; 1521 *resp = rp; 1522 break; 1523 } 1524 1525 case NGM_ASCII2BINARY: 1526 { 1527 int bufSize = 2000; /* XXX hard coded constant */ 1528 const struct ng_cmdlist *c; 1529 const struct ng_parse_type *argstype; 1530 struct ng_mesg *rp, *ascii, *binary; 1531 int off = 0; 1532 1533 /* Data area must contain at least a struct ng_mesg + '\0' */ 1534 ascii = (struct ng_mesg *)msg->data; 1535 if (msg->header.arglen < sizeof(*ascii) + 1 1536 || ascii->header.arglen < 1 1537 || msg->header.arglen 1538 < sizeof(*ascii) + ascii->header.arglen) { 1539 error = EINVAL; 1540 break; 1541 } 1542 ascii->data[ascii->header.arglen - 1] = '\0'; 1543 1544 /* Get a response message with lots of room */ 1545 NG_MKRESPONSE(rp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 1546 if (rp == NULL) { 1547 error = ENOMEM; 1548 break; 1549 } 1550 binary = (struct ng_mesg *)rp->data; 1551 1552 /* Copy ASCII message header to response message payload */ 1553 bcopy(ascii, binary, sizeof(*ascii)); 1554 1555 /* Find command by matching ASCII command string */ 1556 for (c = here->type->cmdlist; 1557 c != NULL && c->name != NULL; c++) { 1558 if (strcmp(ascii->header.cmdstr, c->name) == 0) 1559 break; 1560 } 1561 if (c == NULL || c->name == NULL) { 1562 for (c = ng_generic_cmds; c->name != NULL; c++) { 1563 if (strcmp(ascii->header.cmdstr, c->name) == 0) 1564 break; 1565 } 1566 if (c->name == NULL) { 1567 FREE(rp, M_NETGRAPH); 1568 error = ENOSYS; 1569 break; 1570 } 1571 } 1572 1573 /* Convert command name to binary */ 1574 binary->header.cmd = c->cmd; 1575 binary->header.typecookie = c->cookie; 1576 1577 /* Convert command arguments to binary */ 1578 argstype = (binary->header.flags & NGF_RESP) ? 1579 c->respType : c->mesgType; 1580 if (argstype == NULL) 1581 bufSize = 0; 1582 else { 1583 if ((error = ng_parse(argstype, ascii->data, 1584 &off, (u_char *)binary->data, &bufSize)) != 0) { 1585 FREE(rp, M_NETGRAPH); 1586 break; 1587 } 1588 } 1589 1590 /* Return the result */ 1591 binary->header.arglen = bufSize; 1592 rp->header.arglen = sizeof(*binary) + bufSize; 1593 *resp = rp; 1594 break; 1595 } 1596 1597 case NGM_TEXT_CONFIG: 1598 case NGM_TEXT_STATUS: 1599 /* 1600 * This one is tricky as it passes the command down to the 1601 * actual node, even though it is a generic type command. 1602 * This means we must assume that the msg is already freed 1603 * when control passes back to us. 1604 */ 1605 if (resp == NULL) { 1606 error = EINVAL; 1607 break; 1608 } 1609 if (here->type->rcvmsg != NULL) 1610 return((*here->type->rcvmsg)(here, msg, retaddr, 1611 resp, lasthook)); 1612 /* Fall through if rcvmsg not supported */ 1613 default: 1614 TRAP_ERROR; 1615 error = EINVAL; 1616 } 1617 FREE(msg, M_NETGRAPH); 1618 return (error); 1619 } 1620 1621 /* 1622 * Send a data packet to a node. If the recipient has no 1623 * 'receive data' method, then silently discard the packet. 1624 */ 1625 int 1626 ng_send_data(hook_p hook, struct mbuf *m, meta_p meta, 1627 struct mbuf **ret_m, meta_p *ret_meta) 1628 { 1629 ng_rcvdata_t *rcvdata; 1630 int error; 1631 1632 CHECK_DATA_MBUF(m); 1633 if (hook && (hook->flags & HK_INVALID) == 0) { 1634 rcvdata = hook->peer->node->type->rcvdata; 1635 if (rcvdata != NULL) 1636 error = (*rcvdata)(hook->peer, m, meta, 1637 ret_m, ret_meta); 1638 else { 1639 error = 0; 1640 NG_FREE_DATA(m, meta); 1641 } 1642 } else { 1643 TRAP_ERROR; 1644 error = ENOTCONN; 1645 NG_FREE_DATA(m, meta); 1646 } 1647 return (error); 1648 } 1649 1650 /* 1651 * Send a queued data packet to a node. If the recipient has no 1652 * 'receive queued data' method, then try the 'receive data' method above. 1653 */ 1654 int 1655 ng_send_dataq(hook_p hook, struct mbuf *m, meta_p meta, 1656 struct mbuf **ret_m, meta_p *ret_meta) 1657 { 1658 ng_rcvdata_t *rcvdata; 1659 int error; 1660 1661 CHECK_DATA_MBUF(m); 1662 if (hook && (hook->flags & HK_INVALID) == 0) { 1663 rcvdata = hook->peer->node->type->rcvdataq; 1664 if (rcvdata != NULL) 1665 error = (*rcvdata)(hook->peer, m, meta, 1666 ret_m, ret_meta); 1667 else { 1668 rcvdata = hook->peer->node->type->rcvdata; 1669 if (rcvdata != NULL) { 1670 error = (*rcvdata)(hook->peer, m, meta, 1671 ret_m, ret_meta); 1672 } else { 1673 error = 0; 1674 NG_FREE_DATA(m, meta); 1675 } 1676 } 1677 } else { 1678 TRAP_ERROR; 1679 error = ENOTCONN; 1680 NG_FREE_DATA(m, meta); 1681 } 1682 return (error); 1683 } 1684 1685 /* 1686 * Copy a 'meta'. 1687 * 1688 * Returns new meta, or NULL if original meta is NULL or ENOMEM. 1689 */ 1690 meta_p 1691 ng_copy_meta(meta_p meta) 1692 { 1693 meta_p meta2; 1694 1695 if (meta == NULL) 1696 return (NULL); 1697 MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH, M_NOWAIT); 1698 if (meta2 == NULL) 1699 return (NULL); 1700 meta2->allocated_len = meta->used_len; 1701 bcopy(meta, meta2, meta->used_len); 1702 return (meta2); 1703 } 1704 1705 /************************************************************************ 1706 Module routines 1707 ************************************************************************/ 1708 1709 /* 1710 * Handle the loading/unloading of a netgraph node type module 1711 */ 1712 int 1713 ng_mod_event(module_t mod, int event, void *data) 1714 { 1715 struct ng_type *const type = data; 1716 int s, error = 0; 1717 1718 switch (event) { 1719 case MOD_LOAD: 1720 1721 /* Register new netgraph node type */ 1722 s = splnet(); 1723 if ((error = ng_newtype(type)) != 0) { 1724 splx(s); 1725 break; 1726 } 1727 1728 /* Call type specific code */ 1729 if (type->mod_event != NULL) 1730 if ((error = (*type->mod_event)(mod, event, data)) != 0) 1731 LIST_REMOVE(type, types); 1732 splx(s); 1733 break; 1734 1735 case MOD_UNLOAD: 1736 s = splnet(); 1737 if (type->refs != 0) /* make sure no nodes exist! */ 1738 error = EBUSY; 1739 else { 1740 if (type->mod_event != NULL) { /* check with type */ 1741 error = (*type->mod_event)(mod, event, data); 1742 if (error != 0) { /* type refuses.. */ 1743 splx(s); 1744 break; 1745 } 1746 } 1747 LIST_REMOVE(type, types); 1748 } 1749 splx(s); 1750 break; 1751 1752 default: 1753 if (type->mod_event != NULL) 1754 error = (*type->mod_event)(mod, event, data); 1755 else 1756 error = 0; /* XXX ? */ 1757 break; 1758 } 1759 return (error); 1760 } 1761 1762 /* 1763 * Handle loading and unloading for this code. 1764 * The only thing we need to link into is the NETISR strucure. 1765 */ 1766 static int 1767 ngb_mod_event(module_t mod, int event, void *data) 1768 { 1769 int s, error = 0; 1770 1771 switch (event) { 1772 case MOD_LOAD: 1773 /* Register line discipline */ 1774 s = splimp(); 1775 error = register_netisr(NETISR_NETGRAPH, ngintr); 1776 splx(s); 1777 break; 1778 case MOD_UNLOAD: 1779 /* You cant unload it because an interface may be using it. */ 1780 error = EBUSY; 1781 break; 1782 default: 1783 error = EOPNOTSUPP; 1784 break; 1785 } 1786 return (error); 1787 } 1788 1789 static moduledata_t netgraph_mod = { 1790 "netgraph", 1791 ngb_mod_event, 1792 (NULL) 1793 }; 1794 DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 1795 1796 /************************************************************************ 1797 Queueing routines 1798 ************************************************************************/ 1799 1800 /* The structure for queueing across ISR switches */ 1801 struct ng_queue_entry { 1802 u_long flags; 1803 struct ng_queue_entry *next; 1804 union { 1805 struct { 1806 hook_p da_hook; /* target hook */ 1807 struct mbuf *da_m; 1808 meta_p da_meta; 1809 } data; 1810 struct { 1811 struct ng_mesg *msg_msg; 1812 node_p msg_node; 1813 void *msg_retaddr; 1814 hook_p msg_lasthook; 1815 } msg; 1816 } body; 1817 }; 1818 #define NGQF_DATA 0x01 /* the queue element is data */ 1819 #define NGQF_MESG 0x02 /* the queue element is a message */ 1820 1821 static struct ng_queue_entry *ngqbase; /* items to be unqueued */ 1822 static struct ng_queue_entry *ngqlast; /* last item queued */ 1823 static const int ngqroom = 64; /* max items to queue */ 1824 static int ngqsize; /* number of items in queue */ 1825 1826 static struct ng_queue_entry *ngqfree; /* free ones */ 1827 static const int ngqfreemax = 16;/* cache at most this many */ 1828 static int ngqfreesize; /* number of cached entries */ 1829 1830 /* 1831 * Get a queue entry 1832 */ 1833 static struct ng_queue_entry * 1834 ng_getqblk(void) 1835 { 1836 register struct ng_queue_entry *q; 1837 int s; 1838 1839 /* Could be guarding against tty ints or whatever */ 1840 s = splhigh(); 1841 1842 /* Try get a cached queue block, or else allocate a new one */ 1843 if ((q = ngqfree) == NULL) { 1844 splx(s); 1845 if (ngqsize < ngqroom) { /* don't worry about races */ 1846 MALLOC(q, struct ng_queue_entry *, 1847 sizeof(*q), M_NETGRAPH, M_NOWAIT); 1848 } 1849 } else { 1850 ngqfree = q->next; 1851 ngqfreesize--; 1852 splx(s); 1853 } 1854 return (q); 1855 } 1856 1857 /* 1858 * Release a queue entry 1859 */ 1860 #define RETURN_QBLK(q) \ 1861 do { \ 1862 int s; \ 1863 if (ngqfreesize < ngqfreemax) { /* don't worry about races */ \ 1864 s = splhigh(); \ 1865 (q)->next = ngqfree; \ 1866 ngqfree = (q); \ 1867 ngqfreesize++; \ 1868 splx(s); \ 1869 } else { \ 1870 FREE((q), M_NETGRAPH); \ 1871 } \ 1872 } while (0) 1873 1874 /* 1875 * Running at a raised (but we don't know which) processor priority level, 1876 * put the data onto a queue to be picked up by another PPL (probably splnet) 1877 */ 1878 int 1879 ng_queue_data(hook_p hook, struct mbuf *m, meta_p meta) 1880 { 1881 struct ng_queue_entry *q; 1882 int s; 1883 1884 if (hook == NULL) { 1885 NG_FREE_DATA(m, meta); 1886 return (0); 1887 } 1888 if ((q = ng_getqblk()) == NULL) { 1889 NG_FREE_DATA(m, meta); 1890 return (ENOBUFS); 1891 } 1892 1893 /* Fill out the contents */ 1894 q->flags = NGQF_DATA; 1895 q->next = NULL; 1896 q->body.data.da_hook = hook; 1897 q->body.data.da_m = m; 1898 q->body.data.da_meta = meta; 1899 hook->refs++; /* don't let it go away while on the queue */ 1900 1901 /* Put it on the queue */ 1902 s = splhigh(); 1903 if (ngqbase) { 1904 ngqlast->next = q; 1905 } else { 1906 ngqbase = q; 1907 } 1908 ngqlast = q; 1909 ngqsize++; 1910 splx(s); 1911 1912 /* Schedule software interrupt to handle it later */ 1913 schednetisr(NETISR_NETGRAPH); 1914 return (0); 1915 } 1916 1917 /* 1918 * Running at a raised (but we don't know which) processor priority level, 1919 * put the msg onto a queue to be picked up by another PPL (probably splnet) 1920 */ 1921 int 1922 ng_queue_msg(node_p here, struct ng_mesg *msg, const char *address) 1923 { 1924 register struct ng_queue_entry *q; 1925 int s; 1926 node_p dest = NULL; 1927 char *retaddr = NULL; 1928 int error; 1929 hook_p lasthook = NULL; 1930 1931 /* Find the target node. */ 1932 error = ng_path2node(here, address, &dest, &retaddr, &lasthook); 1933 if (error) { 1934 FREE(msg, M_NETGRAPH); 1935 return (error); 1936 } 1937 if ((q = ng_getqblk()) == NULL) { 1938 FREE(msg, M_NETGRAPH); 1939 if (retaddr) 1940 FREE(retaddr, M_NETGRAPH); 1941 return (ENOBUFS); 1942 } 1943 1944 /* Fill out the contents */ 1945 q->flags = NGQF_MESG; 1946 q->next = NULL; 1947 q->body.msg.msg_node = dest; 1948 q->body.msg.msg_msg = msg; 1949 q->body.msg.msg_retaddr = retaddr; 1950 q->body.msg.msg_lasthook = lasthook; 1951 dest->refs++; /* don't let it go away while on the queue */ 1952 if (lasthook) 1953 lasthook->refs++; /* same for the hook */ 1954 1955 /* Put it on the queue */ 1956 s = splhigh(); 1957 if (ngqbase) { 1958 ngqlast->next = q; 1959 } else { 1960 ngqbase = q; 1961 } 1962 ngqlast = q; 1963 ngqsize++; 1964 splx(s); 1965 1966 /* Schedule software interrupt to handle it later */ 1967 schednetisr(NETISR_NETGRAPH); 1968 return (0); 1969 } 1970 1971 /* 1972 * Pick an item off the queue, process it, and dispose of the queue entry. 1973 * Should be running at splnet. 1974 */ 1975 static void 1976 ngintr(void) 1977 { 1978 hook_p hook; 1979 struct ng_queue_entry *ngq; 1980 struct mbuf *m; 1981 meta_p meta; 1982 void *retaddr; 1983 struct ng_mesg *msg; 1984 node_p node; 1985 int error = 0; 1986 int s; 1987 1988 while (1) { 1989 s = splhigh(); 1990 if ((ngq = ngqbase)) { 1991 ngqbase = ngq->next; 1992 ngqsize--; 1993 } 1994 splx(s); 1995 if (ngq == NULL) 1996 return; 1997 switch (ngq->flags) { 1998 case NGQF_DATA: 1999 hook = ngq->body.data.da_hook; 2000 m = ngq->body.data.da_m; 2001 meta = ngq->body.data.da_meta; 2002 RETURN_QBLK(ngq); 2003 NG_SEND_DATAQ(error, hook, m, meta); 2004 ng_unref_hook(hook); 2005 break; 2006 case NGQF_MESG: 2007 node = ngq->body.msg.msg_node; 2008 msg = ngq->body.msg.msg_msg; 2009 retaddr = ngq->body.msg.msg_retaddr; 2010 hook = ngq->body.msg.msg_lasthook; 2011 RETURN_QBLK(ngq); 2012 if (hook) { 2013 if ((hook->refs == 1) 2014 || (hook->flags & HK_INVALID) != 0) { 2015 /* If the hook only has one ref left 2016 then we can't use it */ 2017 ng_unref_hook(hook); 2018 hook = NULL; 2019 } else { 2020 ng_unref_hook(hook); 2021 } 2022 } 2023 /* similarly, if the node is a zombie.. */ 2024 if (node->flags & NG_INVALID) { 2025 FREE(msg, M_NETGRAPH); 2026 } else { 2027 CALL_MSG_HANDLER(error, node, msg, 2028 retaddr, NULL, hook); 2029 } 2030 ng_unref(node); 2031 if (retaddr) 2032 FREE(retaddr, M_NETGRAPH); 2033 break; 2034 default: 2035 RETURN_QBLK(ngq); 2036 } 2037 } 2038 } 2039 2040 2041