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