ng_base.c revision 70159
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 70159 2000-12-18 20:03:32Z 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), "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_ABI_VERSION) 860 || (namelen == 0) 861 || (namelen > NG_TYPELEN)) { 862 TRAP_ERROR; 863 return (EINVAL); 864 } 865 866 /* Check for name collision */ 867 if (ng_findtype(tp->name) != NULL) { 868 TRAP_ERROR; 869 return (EEXIST); 870 } 871 872 /* Link in new type */ 873 LIST_INSERT_HEAD(&typelist, tp, types); 874 tp->refs = 0; 875 return (0); 876} 877 878/* 879 * Look for a type of the name given 880 */ 881struct ng_type * 882ng_findtype(const char *typename) 883{ 884 struct ng_type *type; 885 886 LIST_FOREACH(type, &typelist, types) { 887 if (strcmp(type->name, typename) == 0) 888 break; 889 } 890 return (type); 891} 892 893 894/************************************************************************ 895 Composite routines 896************************************************************************/ 897 898/* 899 * Make a peer and connect. The order is arranged to minimise 900 * the work needed to back out in case of error. 901 */ 902int 903ng_mkpeer(node_p node, const char *name, const char *name2, char *type) 904{ 905 node_p node2; 906 hook_p hook; 907 hook_p hook2; 908 int error; 909 910 if ((error = ng_add_hook(node, name, &hook))) 911 return (error); 912 if ((error = ng_make_node(type, &node2))) { 913 ng_destroy_hook(hook); 914 return (error); 915 } 916 if ((error = ng_add_hook(node2, name2, &hook2))) { 917 ng_rmnode(node2); 918 ng_destroy_hook(hook); 919 return (error); 920 } 921 922 /* 923 * Actually link the two hooks together.. on failure they are 924 * destroyed so we don't have to do that here. 925 */ 926 if ((error = ng_connect(hook, hook2))) 927 ng_rmnode(node2); 928 return (error); 929} 930 931/* 932 * Connect two nodes using the specified hooks 933 */ 934int 935ng_con_nodes(node_p node, const char *name, node_p node2, const char *name2) 936{ 937 int error; 938 hook_p hook; 939 hook_p hook2; 940 941 if ((error = ng_add_hook(node, name, &hook))) 942 return (error); 943 if ((error = ng_add_hook(node2, name2, &hook2))) { 944 ng_destroy_hook(hook); 945 return (error); 946 } 947 return (ng_connect(hook, hook2)); 948} 949 950/* 951 * Parse and verify a string of the form: <NODE:><PATH> 952 * 953 * Such a string can refer to a specific node or a specific hook 954 * on a specific node, depending on how you look at it. In the 955 * latter case, the PATH component must not end in a dot. 956 * 957 * Both <NODE:> and <PATH> are optional. The <PATH> is a string 958 * of hook names separated by dots. This breaks out the original 959 * string, setting *nodep to "NODE" (or NULL if none) and *pathp 960 * to "PATH" (or NULL if degenerate). Also, *hookp will point to 961 * the final hook component of <PATH>, if any, otherwise NULL. 962 * 963 * This returns -1 if the path is malformed. The char ** are optional. 964 */ 965 966int 967ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 968{ 969 char *node, *path, *hook; 970 int k; 971 972 /* 973 * Extract absolute NODE, if any 974 */ 975 for (path = addr; *path && *path != ':'; path++); 976 if (*path) { 977 node = addr; /* Here's the NODE */ 978 *path++ = '\0'; /* Here's the PATH */ 979 980 /* Node name must not be empty */ 981 if (!*node) 982 return -1; 983 984 /* A name of "." is OK; otherwise '.' not allowed */ 985 if (strcmp(node, ".") != 0) { 986 for (k = 0; node[k]; k++) 987 if (node[k] == '.') 988 return -1; 989 } 990 } else { 991 node = NULL; /* No absolute NODE */ 992 path = addr; /* Here's the PATH */ 993 } 994 995 /* Snoop for illegal characters in PATH */ 996 for (k = 0; path[k]; k++) 997 if (path[k] == ':') 998 return -1; 999 1000 /* Check for no repeated dots in PATH */ 1001 for (k = 0; path[k]; k++) 1002 if (path[k] == '.' && path[k + 1] == '.') 1003 return -1; 1004 1005 /* Remove extra (degenerate) dots from beginning or end of PATH */ 1006 if (path[0] == '.') 1007 path++; 1008 if (*path && path[strlen(path) - 1] == '.') 1009 path[strlen(path) - 1] = 0; 1010 1011 /* If PATH has a dot, then we're not talking about a hook */ 1012 if (*path) { 1013 for (hook = path, k = 0; path[k]; k++) 1014 if (path[k] == '.') { 1015 hook = NULL; 1016 break; 1017 } 1018 } else 1019 path = hook = NULL; 1020 1021 /* Done */ 1022 if (nodep) 1023 *nodep = node; 1024 if (pathp) 1025 *pathp = path; 1026 if (hookp) 1027 *hookp = hook; 1028 return (0); 1029} 1030 1031/* 1032 * Given a path, which may be absolute or relative, and a starting node, 1033 * return the destination node. Compute the "return address" if desired. 1034 */ 1035int 1036ng_path2node(node_p here, const char *address, node_p *destp, hook_p *lasthook) 1037{ 1038 char fullpath[NG_PATHLEN + 1]; 1039 char *nodename, *path, pbuf[2]; 1040 node_p node; 1041 char *cp; 1042 hook_p hook = NULL; 1043 1044 /* Initialize */ 1045 if (destp == NULL) 1046 return EINVAL; 1047 *destp = NULL; 1048 1049 /* Make a writable copy of address for ng_path_parse() */ 1050 strncpy(fullpath, address, sizeof(fullpath) - 1); 1051 fullpath[sizeof(fullpath) - 1] = '\0'; 1052 1053 /* Parse out node and sequence of hooks */ 1054 if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 1055 TRAP_ERROR; 1056 return EINVAL; 1057 } 1058 if (path == NULL) { 1059 pbuf[0] = '.'; /* Needs to be writable */ 1060 pbuf[1] = '\0'; 1061 path = pbuf; 1062 } 1063 1064 /* For an absolute address, jump to the starting node */ 1065 if (nodename) { 1066 node = ng_findname(here, nodename); 1067 if (node == NULL) { 1068 TRAP_ERROR; 1069 return (ENOENT); 1070 } 1071 } else 1072 node = here; 1073 1074 /* Now follow the sequence of hooks */ 1075 for (cp = path; node != NULL && *cp != '\0'; ) { 1076 char *segment; 1077 1078 /* 1079 * Break out the next path segment. Replace the dot we just 1080 * found with a NUL; "cp" points to the next segment (or the 1081 * NUL at the end). 1082 */ 1083 for (segment = cp; *cp != '\0'; cp++) { 1084 if (*cp == '.') { 1085 *cp++ = '\0'; 1086 break; 1087 } 1088 } 1089 1090 /* Empty segment */ 1091 if (*segment == '\0') 1092 continue; 1093 1094 /* We have a segment, so look for a hook by that name */ 1095 hook = ng_findhook(node, segment); 1096 1097 /* Can't get there from here... */ 1098 if (hook == NULL 1099 || hook->peer == NULL 1100 || (hook->flags & HK_INVALID) != 0) { 1101 TRAP_ERROR; 1102 return (ENOENT); 1103 } 1104 1105 /* Hop on over to the next node */ 1106 node = hook->peer->node; 1107 } 1108 1109 /* If node somehow missing, fail here (probably this is not needed) */ 1110 if (node == NULL) { 1111 TRAP_ERROR; 1112 return (ENXIO); 1113 } 1114 1115 /* Done */ 1116 *destp = node; 1117 if (lasthook != NULL) 1118 *lasthook = hook ? hook->peer : NULL; 1119 return (0); 1120} 1121 1122/* 1123 * Call the appropriate message handler for the object. 1124 * It is up to the message handler to free the message. 1125 * If it's a generic message, handle it generically, otherwise 1126 * call the type's message handler (if it exists) 1127 * XXX (race). Remember that a queued message may reference a node 1128 * or hook that has just been invalidated. It will exist 1129 * as the queue code is holding a reference, but.. 1130 */ 1131 1132#define CALL_MSG_HANDLER(error, node, msg, retaddr, resp, hook) \ 1133do { \ 1134 if((msg)->header.typecookie == NGM_GENERIC_COOKIE) { \ 1135 (error) = ng_generic_msg((node), (msg), \ 1136 (retaddr), (resp), (hook)); \ 1137 } else { \ 1138 if ((node)->type->rcvmsg != NULL) { \ 1139 (error) = (*(node)->type->rcvmsg)((node), \ 1140 (msg), (retaddr), (resp), (hook)); \ 1141 } else { \ 1142 TRAP_ERROR; \ 1143 FREE((msg), M_NETGRAPH); \ 1144 (error) = EINVAL; \ 1145 } \ 1146 } \ 1147} while (0) 1148 1149 1150/* 1151 * Send a control message to a node. 1152 * If hook is supplied, use it in preference to the address. 1153 * If the return address is not supplied it will be set to this node. 1154 */ 1155int 1156ng_send_msg(node_p here, struct ng_mesg *msg, const char *address, 1157 hook_p hook, char *retaddr, struct ng_mesg **rptr) 1158{ 1159 node_p dest = NULL; 1160 int error; 1161 hook_p lasthook; 1162 1163 /* 1164 * Find the target node. 1165 * If there is a HOOK argument, then use that in preference 1166 * to the address. 1167 */ 1168 if (hook) { 1169 lasthook = hook->peer; 1170 dest = lasthook->node; 1171 } else { 1172 error = ng_path2node(here, address, &dest, &lasthook); 1173 if (error) { 1174 FREE(msg, M_NETGRAPH); 1175 return (error); 1176 } 1177 } 1178 1179 /* If the user didn't supply a return addres, assume it's "here". */ 1180 if (retaddr == NULL) { 1181 /* 1182 * Now fill out the return address, 1183 * i.e. the name/ID of the sender. (If we didn't get one) 1184 */ 1185 MALLOC(retaddr, char *, NG_NODELEN + 2, M_NETGRAPH, M_NOWAIT); 1186 if (retaddr == NULL) { 1187 TRAP_ERROR; 1188 return (ENOMEM); 1189 } 1190 if (here->name != NULL) 1191 sprintf(retaddr, "%s:", here->name); 1192 else 1193 sprintf(retaddr, "[%x]:", ng_node2ID(here)); 1194 } 1195 1196 /* Make sure the resp field is null before we start */ 1197 if (rptr != NULL) 1198 *rptr = NULL; 1199 1200 CALL_MSG_HANDLER(error, dest, msg, retaddr, rptr, lasthook); 1201 1202 /* Make sure that if there is a response, it has the RESP bit set */ 1203 if ((error == 0) && rptr && *rptr) 1204 (*rptr)->header.flags |= NGF_RESP; 1205 1206 /* 1207 * If we had a return address it is up to us to free it. They should 1208 * have taken a copy if they needed to make a delayed response. 1209 */ 1210 if (retaddr) 1211 FREE(retaddr, M_NETGRAPH); 1212 return (error); 1213} 1214 1215/* 1216 * Implement the 'generic' control messages 1217 */ 1218static int 1219ng_generic_msg(node_p here, struct ng_mesg *msg, const char *retaddr, 1220 struct ng_mesg **resp, hook_p lasthook) 1221{ 1222 int error = 0; 1223 1224 if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 1225 TRAP_ERROR; 1226 FREE(msg, M_NETGRAPH); 1227 return (EINVAL); 1228 } 1229 switch (msg->header.cmd) { 1230 case NGM_SHUTDOWN: 1231 ng_rmnode(here); 1232 break; 1233 case NGM_MKPEER: 1234 { 1235 struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 1236 1237 if (msg->header.arglen != sizeof(*mkp)) { 1238 TRAP_ERROR; 1239 return (EINVAL); 1240 } 1241 mkp->type[sizeof(mkp->type) - 1] = '\0'; 1242 mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 1243 mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 1244 error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 1245 break; 1246 } 1247 case NGM_CONNECT: 1248 { 1249 struct ngm_connect *const con = 1250 (struct ngm_connect *) msg->data; 1251 node_p node2; 1252 1253 if (msg->header.arglen != sizeof(*con)) { 1254 TRAP_ERROR; 1255 return (EINVAL); 1256 } 1257 con->path[sizeof(con->path) - 1] = '\0'; 1258 con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 1259 con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 1260 error = ng_path2node(here, con->path, &node2, NULL); 1261 if (error) 1262 break; 1263 error = ng_con_nodes(here, con->ourhook, node2, con->peerhook); 1264 break; 1265 } 1266 case NGM_NAME: 1267 { 1268 struct ngm_name *const nam = (struct ngm_name *) msg->data; 1269 1270 if (msg->header.arglen != sizeof(*nam)) { 1271 TRAP_ERROR; 1272 return (EINVAL); 1273 } 1274 nam->name[sizeof(nam->name) - 1] = '\0'; 1275 error = ng_name_node(here, nam->name); 1276 break; 1277 } 1278 case NGM_RMHOOK: 1279 { 1280 struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 1281 hook_p hook; 1282 1283 if (msg->header.arglen != sizeof(*rmh)) { 1284 TRAP_ERROR; 1285 return (EINVAL); 1286 } 1287 rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 1288 if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 1289 ng_destroy_hook(hook); 1290 break; 1291 } 1292 case NGM_NODEINFO: 1293 { 1294 struct nodeinfo *ni; 1295 struct ng_mesg *rp; 1296 1297 /* Get response struct */ 1298 if (resp == NULL) { 1299 error = EINVAL; 1300 break; 1301 } 1302 NG_MKRESPONSE(rp, msg, sizeof(*ni), M_NOWAIT); 1303 if (rp == NULL) { 1304 error = ENOMEM; 1305 break; 1306 } 1307 1308 /* Fill in node info */ 1309 ni = (struct nodeinfo *) rp->data; 1310 if (here->name != NULL) 1311 strncpy(ni->name, here->name, NG_NODELEN); 1312 strncpy(ni->type, here->type->name, NG_TYPELEN); 1313 ni->id = ng_node2ID(here); 1314 ni->hooks = here->numhooks; 1315 *resp = rp; 1316 break; 1317 } 1318 case NGM_LISTHOOKS: 1319 { 1320 const int nhooks = here->numhooks; 1321 struct hooklist *hl; 1322 struct nodeinfo *ni; 1323 struct ng_mesg *rp; 1324 hook_p hook; 1325 1326 /* Get response struct */ 1327 if (resp == NULL) { 1328 error = EINVAL; 1329 break; 1330 } 1331 NG_MKRESPONSE(rp, msg, sizeof(*hl) 1332 + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 1333 if (rp == NULL) { 1334 error = ENOMEM; 1335 break; 1336 } 1337 hl = (struct hooklist *) rp->data; 1338 ni = &hl->nodeinfo; 1339 1340 /* Fill in node info */ 1341 if (here->name) 1342 strncpy(ni->name, here->name, NG_NODELEN); 1343 strncpy(ni->type, here->type->name, NG_TYPELEN); 1344 ni->id = ng_node2ID(here); 1345 1346 /* Cycle through the linked list of hooks */ 1347 ni->hooks = 0; 1348 LIST_FOREACH(hook, &here->hooks, hooks) { 1349 struct linkinfo *const link = &hl->link[ni->hooks]; 1350 1351 if (ni->hooks >= nhooks) { 1352 log(LOG_ERR, "%s: number of %s changed\n", 1353 __FUNCTION__, "hooks"); 1354 break; 1355 } 1356 if ((hook->flags & HK_INVALID) != 0) 1357 continue; 1358 strncpy(link->ourhook, hook->name, NG_HOOKLEN); 1359 strncpy(link->peerhook, hook->peer->name, NG_HOOKLEN); 1360 if (hook->peer->node->name != NULL) 1361 strncpy(link->nodeinfo.name, 1362 hook->peer->node->name, NG_NODELEN); 1363 strncpy(link->nodeinfo.type, 1364 hook->peer->node->type->name, NG_TYPELEN); 1365 link->nodeinfo.id = ng_node2ID(hook->peer->node); 1366 link->nodeinfo.hooks = hook->peer->node->numhooks; 1367 ni->hooks++; 1368 } 1369 *resp = rp; 1370 break; 1371 } 1372 1373 case NGM_LISTNAMES: 1374 case NGM_LISTNODES: 1375 { 1376 const int unnamed = (msg->header.cmd == NGM_LISTNODES); 1377 struct namelist *nl; 1378 struct ng_mesg *rp; 1379 node_p node; 1380 int num = 0; 1381 1382 if (resp == NULL) { 1383 error = EINVAL; 1384 break; 1385 } 1386 1387 /* Count number of nodes */ 1388 LIST_FOREACH(node, &nodelist, nodes) { 1389 if (unnamed || node->name != NULL) 1390 num++; 1391 } 1392 1393 /* Get response struct */ 1394 if (resp == NULL) { 1395 error = EINVAL; 1396 break; 1397 } 1398 NG_MKRESPONSE(rp, msg, sizeof(*nl) 1399 + (num * sizeof(struct nodeinfo)), M_NOWAIT); 1400 if (rp == NULL) { 1401 error = ENOMEM; 1402 break; 1403 } 1404 nl = (struct namelist *) rp->data; 1405 1406 /* Cycle through the linked list of nodes */ 1407 nl->numnames = 0; 1408 LIST_FOREACH(node, &nodelist, nodes) { 1409 struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; 1410 1411 if (nl->numnames >= num) { 1412 log(LOG_ERR, "%s: number of %s changed\n", 1413 __FUNCTION__, "nodes"); 1414 break; 1415 } 1416 if ((node->flags & NG_INVALID) != 0) 1417 continue; 1418 if (!unnamed && node->name == NULL) 1419 continue; 1420 if (node->name != NULL) 1421 strncpy(np->name, node->name, NG_NODELEN); 1422 strncpy(np->type, node->type->name, NG_TYPELEN); 1423 np->id = ng_node2ID(node); 1424 np->hooks = node->numhooks; 1425 nl->numnames++; 1426 } 1427 *resp = rp; 1428 break; 1429 } 1430 1431 case NGM_LISTTYPES: 1432 { 1433 struct typelist *tl; 1434 struct ng_mesg *rp; 1435 struct ng_type *type; 1436 int num = 0; 1437 1438 if (resp == NULL) { 1439 error = EINVAL; 1440 break; 1441 } 1442 1443 /* Count number of types */ 1444 LIST_FOREACH(type, &typelist, types) 1445 num++; 1446 1447 /* Get response struct */ 1448 if (resp == NULL) { 1449 error = EINVAL; 1450 break; 1451 } 1452 NG_MKRESPONSE(rp, msg, sizeof(*tl) 1453 + (num * sizeof(struct typeinfo)), M_NOWAIT); 1454 if (rp == NULL) { 1455 error = ENOMEM; 1456 break; 1457 } 1458 tl = (struct typelist *) rp->data; 1459 1460 /* Cycle through the linked list of types */ 1461 tl->numtypes = 0; 1462 LIST_FOREACH(type, &typelist, types) { 1463 struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 1464 1465 if (tl->numtypes >= num) { 1466 log(LOG_ERR, "%s: number of %s changed\n", 1467 __FUNCTION__, "types"); 1468 break; 1469 } 1470 strncpy(tp->type_name, type->name, NG_TYPELEN); 1471 tp->numnodes = type->refs; 1472 tl->numtypes++; 1473 } 1474 *resp = rp; 1475 break; 1476 } 1477 1478 case NGM_BINARY2ASCII: 1479 { 1480 int bufSize = 20 * 1024; /* XXX hard coded constant */ 1481 const struct ng_parse_type *argstype; 1482 const struct ng_cmdlist *c; 1483 struct ng_mesg *rp, *binary, *ascii; 1484 1485 /* Data area must contain a valid netgraph message */ 1486 binary = (struct ng_mesg *)msg->data; 1487 if (msg->header.arglen < sizeof(struct ng_mesg) 1488 || msg->header.arglen - sizeof(struct ng_mesg) 1489 < binary->header.arglen) { 1490 error = EINVAL; 1491 break; 1492 } 1493 1494 /* Check response pointer */ 1495 if (resp == NULL) { 1496 error = EINVAL; 1497 break; 1498 } 1499 1500 /* Get a response message with lots of room */ 1501 NG_MKRESPONSE(rp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 1502 if (rp == NULL) { 1503 error = ENOMEM; 1504 break; 1505 } 1506 ascii = (struct ng_mesg *)rp->data; 1507 1508 /* Copy binary message header to response message payload */ 1509 bcopy(binary, ascii, sizeof(*binary)); 1510 1511 /* Find command by matching typecookie and command number */ 1512 for (c = here->type->cmdlist; 1513 c != NULL && c->name != NULL; c++) { 1514 if (binary->header.typecookie == c->cookie 1515 && binary->header.cmd == c->cmd) 1516 break; 1517 } 1518 if (c == NULL || c->name == NULL) { 1519 for (c = ng_generic_cmds; c->name != NULL; c++) { 1520 if (binary->header.typecookie == c->cookie 1521 && binary->header.cmd == c->cmd) 1522 break; 1523 } 1524 if (c->name == NULL) { 1525 FREE(rp, M_NETGRAPH); 1526 error = ENOSYS; 1527 break; 1528 } 1529 } 1530 1531 /* Convert command name to ASCII */ 1532 snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 1533 "%s", c->name); 1534 1535 /* Convert command arguments to ASCII */ 1536 argstype = (binary->header.flags & NGF_RESP) ? 1537 c->respType : c->mesgType; 1538 if (argstype == NULL) 1539 *ascii->data = '\0'; 1540 else { 1541 if ((error = ng_unparse(argstype, 1542 (u_char *)binary->data, 1543 ascii->data, bufSize)) != 0) { 1544 FREE(rp, M_NETGRAPH); 1545 break; 1546 } 1547 } 1548 1549 /* Return the result as struct ng_mesg plus ASCII string */ 1550 bufSize = strlen(ascii->data) + 1; 1551 ascii->header.arglen = bufSize; 1552 rp->header.arglen = sizeof(*ascii) + bufSize; 1553 *resp = rp; 1554 break; 1555 } 1556 1557 case NGM_ASCII2BINARY: 1558 { 1559 int bufSize = 2000; /* XXX hard coded constant */ 1560 const struct ng_cmdlist *c; 1561 const struct ng_parse_type *argstype; 1562 struct ng_mesg *rp, *ascii, *binary; 1563 int off = 0; 1564 1565 /* Data area must contain at least a struct ng_mesg + '\0' */ 1566 ascii = (struct ng_mesg *)msg->data; 1567 if (msg->header.arglen < sizeof(*ascii) + 1 1568 || ascii->header.arglen < 1 1569 || msg->header.arglen 1570 < sizeof(*ascii) + ascii->header.arglen) { 1571 error = EINVAL; 1572 break; 1573 } 1574 ascii->data[ascii->header.arglen - 1] = '\0'; 1575 1576 /* Check response pointer */ 1577 if (resp == NULL) { 1578 error = EINVAL; 1579 break; 1580 } 1581 1582 /* Get a response message with lots of room */ 1583 NG_MKRESPONSE(rp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 1584 if (rp == NULL) { 1585 error = ENOMEM; 1586 break; 1587 } 1588 binary = (struct ng_mesg *)rp->data; 1589 1590 /* Copy ASCII message header to response message payload */ 1591 bcopy(ascii, binary, sizeof(*ascii)); 1592 1593 /* Find command by matching ASCII command string */ 1594 for (c = here->type->cmdlist; 1595 c != NULL && c->name != NULL; c++) { 1596 if (strcmp(ascii->header.cmdstr, c->name) == 0) 1597 break; 1598 } 1599 if (c == NULL || c->name == NULL) { 1600 for (c = ng_generic_cmds; c->name != NULL; c++) { 1601 if (strcmp(ascii->header.cmdstr, c->name) == 0) 1602 break; 1603 } 1604 if (c->name == NULL) { 1605 FREE(rp, M_NETGRAPH); 1606 error = ENOSYS; 1607 break; 1608 } 1609 } 1610 1611 /* Convert command name to binary */ 1612 binary->header.cmd = c->cmd; 1613 binary->header.typecookie = c->cookie; 1614 1615 /* Convert command arguments to binary */ 1616 argstype = (binary->header.flags & NGF_RESP) ? 1617 c->respType : c->mesgType; 1618 if (argstype == NULL) 1619 bufSize = 0; 1620 else { 1621 if ((error = ng_parse(argstype, ascii->data, 1622 &off, (u_char *)binary->data, &bufSize)) != 0) { 1623 FREE(rp, M_NETGRAPH); 1624 break; 1625 } 1626 } 1627 1628 /* Return the result */ 1629 binary->header.arglen = bufSize; 1630 rp->header.arglen = sizeof(*binary) + bufSize; 1631 *resp = rp; 1632 break; 1633 } 1634 1635 case NGM_TEXT_CONFIG: 1636 case NGM_TEXT_STATUS: 1637 /* 1638 * This one is tricky as it passes the command down to the 1639 * actual node, even though it is a generic type command. 1640 * This means we must assume that the msg is already freed 1641 * when control passes back to us. 1642 */ 1643 if (resp == NULL) { 1644 error = EINVAL; 1645 break; 1646 } 1647 if (here->type->rcvmsg != NULL) 1648 return((*here->type->rcvmsg)(here, msg, retaddr, 1649 resp, lasthook)); 1650 /* Fall through if rcvmsg not supported */ 1651 default: 1652 TRAP_ERROR; 1653 error = EINVAL; 1654 } 1655 FREE(msg, M_NETGRAPH); 1656 return (error); 1657} 1658 1659/* 1660 * Send a data packet to a node. If the recipient has no 1661 * 'receive data' method, then silently discard the packet. 1662 * The receiving node may elect to put the data onto the netgraph 1663 * NETISR queue for later delivery. It may do this because it knows there 1664 * is some recursion and wishes to unwind the stack, or because it has 1665 * some suspicion that it is being called at (say) splimp instead of 1666 * splnet. 1667 */ 1668int 1669ng_send_data(hook_p hook, struct mbuf *m, meta_p meta, 1670 struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) 1671{ 1672 ng_rcvdata_t *rcvdata; 1673 1674 CHECK_DATA_MBUF(m); 1675 if ((hook == NULL) 1676 || ((hook->flags & HK_INVALID) != 0) 1677 || ((rcvdata = hook->peer->node->type->rcvdata) == NULL)) { 1678 TRAP_ERROR; 1679 NG_FREE_DATA(m, meta); 1680 return (ENOTCONN); 1681 } 1682 if (hook->peer->flags & HK_QUEUE) { 1683 return (ng_queue_data(hook, m, meta)); 1684 } 1685 return ( (*rcvdata)(hook->peer, m, meta, ret_m, ret_meta, resp)); 1686} 1687 1688/* 1689 * Send a queued data packet to a node. 1690 * 1691 * This is meant for data that is being dequeued and should therefore NOT 1692 * be queued again. It ignores the queue flag and should NOT be called 1693 * outside of this file. (thus it is static) 1694 */ 1695static int 1696ng_send_data_dont_queue(hook_p hook, struct mbuf *m, meta_p meta, 1697 struct mbuf **ret_m, meta_p *ret_meta, struct ng_mesg **resp) 1698{ 1699 ng_rcvdata_t *rcvdata; 1700 1701 CHECK_DATA_MBUF(m); 1702 if ((hook == NULL) 1703 || ((hook->flags & HK_INVALID) != 0) 1704 || ((rcvdata = hook->peer->node->type->rcvdata) == NULL)) { 1705 TRAP_ERROR; 1706 NG_FREE_DATA(m, meta); 1707 return (ENOTCONN); 1708 } 1709 return ((*rcvdata)(hook->peer, m, meta, ret_m, ret_meta, resp)); 1710} 1711 1712/* 1713 * Copy a 'meta'. 1714 * 1715 * Returns new meta, or NULL if original meta is NULL or ENOMEM. 1716 */ 1717meta_p 1718ng_copy_meta(meta_p meta) 1719{ 1720 meta_p meta2; 1721 1722 if (meta == NULL) 1723 return (NULL); 1724 MALLOC(meta2, meta_p, meta->used_len, M_NETGRAPH, M_NOWAIT); 1725 if (meta2 == NULL) 1726 return (NULL); 1727 meta2->allocated_len = meta->used_len; 1728 bcopy(meta, meta2, meta->used_len); 1729 return (meta2); 1730} 1731 1732/************************************************************************ 1733 Module routines 1734************************************************************************/ 1735 1736/* 1737 * Handle the loading/unloading of a netgraph node type module 1738 */ 1739int 1740ng_mod_event(module_t mod, int event, void *data) 1741{ 1742 struct ng_type *const type = data; 1743 int s, error = 0; 1744 1745 switch (event) { 1746 case MOD_LOAD: 1747 1748 /* Register new netgraph node type */ 1749 s = splnet(); 1750 if ((error = ng_newtype(type)) != 0) { 1751 splx(s); 1752 break; 1753 } 1754 1755 /* Call type specific code */ 1756 if (type->mod_event != NULL) 1757 if ((error = (*type->mod_event)(mod, event, data)) != 0) 1758 LIST_REMOVE(type, types); 1759 splx(s); 1760 break; 1761 1762 case MOD_UNLOAD: 1763 s = splnet(); 1764 if (type->refs != 0) /* make sure no nodes exist! */ 1765 error = EBUSY; 1766 else { 1767 if (type->mod_event != NULL) { /* check with type */ 1768 error = (*type->mod_event)(mod, event, data); 1769 if (error != 0) { /* type refuses.. */ 1770 splx(s); 1771 break; 1772 } 1773 } 1774 LIST_REMOVE(type, types); 1775 } 1776 splx(s); 1777 break; 1778 1779 default: 1780 if (type->mod_event != NULL) 1781 error = (*type->mod_event)(mod, event, data); 1782 else 1783 error = 0; /* XXX ? */ 1784 break; 1785 } 1786 return (error); 1787} 1788 1789/* 1790 * Handle loading and unloading for this code. 1791 * The only thing we need to link into is the NETISR strucure. 1792 */ 1793static int 1794ngb_mod_event(module_t mod, int event, void *data) 1795{ 1796 int s, error = 0; 1797 1798 switch (event) { 1799 case MOD_LOAD: 1800 /* Register line discipline */ 1801 s = splimp(); 1802 error = register_netisr(NETISR_NETGRAPH, ngintr); 1803 splx(s); 1804 break; 1805 case MOD_UNLOAD: 1806 /* You cant unload it because an interface may be using it. */ 1807 error = EBUSY; 1808 break; 1809 default: 1810 error = EOPNOTSUPP; 1811 break; 1812 } 1813 return (error); 1814} 1815 1816static moduledata_t netgraph_mod = { 1817 "netgraph", 1818 ngb_mod_event, 1819 (NULL) 1820}; 1821DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 1822 1823/************************************************************************ 1824 Queueing routines 1825************************************************************************/ 1826 1827/* The structure for queueing across ISR switches */ 1828struct ng_queue_entry { 1829 u_long flags; 1830 struct ng_queue_entry *next; 1831 union { 1832 struct { 1833 hook_p da_hook; /* target hook */ 1834 struct mbuf *da_m; 1835 meta_p da_meta; 1836 } data; 1837 struct { 1838 struct ng_mesg *msg_msg; 1839 node_p msg_node; 1840 hook_p msg_lasthook; 1841 char *msg_retaddr; 1842 } msg; 1843 } body; 1844}; 1845#define NGQF_DATA 0x01 /* the queue element is data */ 1846#define NGQF_MESG 0x02 /* the queue element is a message */ 1847 1848static struct ng_queue_entry *ngqbase; /* items to be unqueued */ 1849static struct ng_queue_entry *ngqlast; /* last item queued */ 1850static const int ngqroom = 64; /* max items to queue */ 1851static int ngqsize; /* number of items in queue */ 1852 1853static struct ng_queue_entry *ngqfree; /* free ones */ 1854static const int ngqfreemax = 16;/* cache at most this many */ 1855static int ngqfreesize; /* number of cached entries */ 1856 1857/* 1858 * Get a queue entry 1859 */ 1860static struct ng_queue_entry * 1861ng_getqblk(void) 1862{ 1863 register struct ng_queue_entry *q; 1864 int s; 1865 1866 /* Could be guarding against tty ints or whatever */ 1867 s = splhigh(); 1868 1869 /* Try get a cached queue block, or else allocate a new one */ 1870 if ((q = ngqfree) == NULL) { 1871 splx(s); 1872 if (ngqsize < ngqroom) { /* don't worry about races */ 1873 MALLOC(q, struct ng_queue_entry *, 1874 sizeof(*q), M_NETGRAPH, M_NOWAIT); 1875 } 1876 } else { 1877 ngqfree = q->next; 1878 ngqfreesize--; 1879 splx(s); 1880 } 1881 return (q); 1882} 1883 1884/* 1885 * Release a queue entry 1886 */ 1887#define RETURN_QBLK(q) \ 1888do { \ 1889 int s; \ 1890 if (ngqfreesize < ngqfreemax) { /* don't worry about races */ \ 1891 s = splhigh(); \ 1892 (q)->next = ngqfree; \ 1893 ngqfree = (q); \ 1894 ngqfreesize++; \ 1895 splx(s); \ 1896 } else { \ 1897 FREE((q), M_NETGRAPH); \ 1898 } \ 1899} while (0) 1900 1901/* 1902 * Running at a raised (but we don't know which) processor priority level, 1903 * put the data onto a queue to be picked up by another PPL (probably splnet) 1904 */ 1905int 1906ng_queue_data(hook_p hook, struct mbuf *m, meta_p meta) 1907{ 1908 struct ng_queue_entry *q; 1909 int s; 1910 1911 if (hook == NULL) { 1912 NG_FREE_DATA(m, meta); 1913 return (0); 1914 } 1915 if ((q = ng_getqblk()) == NULL) { 1916 NG_FREE_DATA(m, meta); 1917 return (ENOBUFS); 1918 } 1919 1920 /* Fill out the contents */ 1921 q->flags = NGQF_DATA; 1922 q->next = NULL; 1923 q->body.data.da_hook = hook; 1924 q->body.data.da_m = m; 1925 q->body.data.da_meta = meta; 1926 s = splhigh(); /* protect refs and queue */ 1927 hook->refs++; /* don't let it go away while on the queue */ 1928 1929 /* Put it on the queue */ 1930 if (ngqbase) { 1931 ngqlast->next = q; 1932 } else { 1933 ngqbase = q; 1934 } 1935 ngqlast = q; 1936 ngqsize++; 1937 splx(s); 1938 1939 /* Schedule software interrupt to handle it later */ 1940 schednetisr(NETISR_NETGRAPH); 1941 return (0); 1942} 1943 1944/* 1945 * Running at a raised (but we don't know which) processor priority level, 1946 * put the msg onto a queue to be picked up by another PPL (probably splnet) 1947 * Either specify an address, or a hook to traverse. 1948 * The return address can be specified, or it will be pointed at this node. 1949 */ 1950int 1951ng_queue_msg(node_p here, struct ng_mesg *msg, const char *address, hook_p hook,char *retaddr) 1952{ 1953 register struct ng_queue_entry *q; 1954 int s; 1955 node_p dest = NULL; 1956 int error; 1957 hook_p lasthook = NULL; 1958 1959 /* 1960 * Find the target node. 1961 * If there is a HOOK argument, then use that in preference 1962 * to the address. 1963 */ 1964 if (hook) { 1965 lasthook = hook->peer; 1966 dest = lasthook->node; 1967 } else { 1968 error = ng_path2node(here, address, &dest, &lasthook); 1969 if (error) { 1970 FREE(msg, M_NETGRAPH); 1971 return (error); 1972 } 1973 } 1974 1975 if (retaddr == NULL) { 1976 /* 1977 * Now fill out the return address, 1978 * i.e. the name/ID of the sender. (If we didn't get one) 1979 */ 1980 MALLOC(retaddr, char *, NG_NODELEN + 2, M_NETGRAPH, M_NOWAIT); 1981 if (retaddr == NULL) { 1982 TRAP_ERROR; 1983 return (ENOMEM); 1984 } 1985 if (here->name != NULL) 1986 sprintf(retaddr, "%s:", here->name); 1987 else 1988 sprintf(retaddr, "[%x]:", ng_node2ID(here)); 1989 } 1990 1991 if ((q = ng_getqblk()) == NULL) { 1992 FREE(msg, M_NETGRAPH); 1993 if (retaddr) 1994 FREE(retaddr, M_NETGRAPH); 1995 return (ENOBUFS); 1996 } 1997 1998 /* Fill out the contents */ 1999 q->flags = NGQF_MESG; 2000 q->next = NULL; 2001 q->body.msg.msg_node = dest; 2002 q->body.msg.msg_msg = msg; 2003 q->body.msg.msg_retaddr = retaddr; /* XXX malloc'd, give it away */ 2004 q->body.msg.msg_lasthook = lasthook; /* XXX needs reference */ 2005 s = splhigh(); /* protect refs and queue */ 2006 dest->refs++; /* don't let it go away while on the queue */ 2007 if (lasthook) 2008 lasthook->refs++; /* same for the hook */ 2009 2010 /* Put it on the queue */ 2011 if (ngqbase) { 2012 ngqlast->next = q; 2013 } else { 2014 ngqbase = q; 2015 } 2016 ngqlast = q; 2017 ngqsize++; 2018 splx(s); 2019 2020 /* Schedule software interrupt to handle it later */ 2021 schednetisr(NETISR_NETGRAPH); 2022 return (0); 2023} 2024 2025/* 2026 * Pick an item off the queue, process it, and dispose of the queue entry. 2027 * Should be running at splnet. 2028 */ 2029static void 2030ngintr(void) 2031{ 2032 hook_p hook; 2033 struct ng_queue_entry *ngq; 2034 struct mbuf *m; 2035 meta_p meta; 2036 void *retaddr; 2037 struct ng_mesg *msg; 2038 node_p node; 2039 int error = 0; 2040 int s; 2041 2042 while (1) { 2043 s = splhigh(); 2044 if ((ngq = ngqbase)) { 2045 ngqbase = ngq->next; 2046 ngqsize--; 2047 } 2048 splx(s); 2049 if (ngq == NULL) 2050 return; 2051 switch (ngq->flags) { 2052 case NGQF_DATA: 2053 hook = ngq->body.data.da_hook; 2054 m = ngq->body.data.da_m; 2055 meta = ngq->body.data.da_meta; 2056 RETURN_QBLK(ngq); 2057 ng_send_data_dont_queue(hook, m, meta, 2058 NULL, NULL, NULL); 2059 m = NULL; 2060 meta = NULL; 2061 ng_unref_hook(hook); 2062 break; 2063 case NGQF_MESG: 2064 node = ngq->body.msg.msg_node; 2065 msg = ngq->body.msg.msg_msg; 2066 retaddr = ngq->body.msg.msg_retaddr; 2067 hook = ngq->body.msg.msg_lasthook; 2068 RETURN_QBLK(ngq); 2069 if (hook) { 2070 if ((hook->flags & HK_INVALID) != 0) { 2071 /* If the hook has been zapped 2072 then we can't use it */ 2073 ng_unref_hook(hook); 2074 hook = NULL; 2075 } 2076 } 2077 /* similarly, if the node is a zombie.. */ 2078 if (node->flags & NG_INVALID) { 2079 FREE(msg, M_NETGRAPH); 2080 } else { 2081 CALL_MSG_HANDLER(error, node, msg, 2082 retaddr, NULL, hook); 2083 } 2084 if (hook) 2085 ng_unref_hook(hook); 2086 ng_unref(node); 2087 if (retaddr) 2088 FREE(retaddr, M_NETGRAPH); 2089 break; 2090 default: 2091 RETURN_QBLK(ngq); 2092 } 2093 } 2094} 2095 2096 2097