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