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