ng_base.c revision 54096
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 54096 1999-12-03 21:17:30Z 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 if (ng_findhook(node, name) != NULL) { 666 TRAP_ERROR; 667 return (EEXIST); 668 } 669 670 /* Allocate the hook and link it up */ 671 MALLOC(hook, hook_p, sizeof(*hook), M_NETGRAPH, M_WAITOK); 672 if (hook == NULL) { 673 TRAP_ERROR; 674 return (ENOMEM); 675 } 676 bzero(hook, sizeof(*hook)); 677 hook->refs = 1; 678 hook->flags = HK_INVALID; 679 hook->node = node; 680 node->refs++; /* each hook counts as a reference */ 681 682 /* Check if the node type code has something to say about it */ 683 if (node->type->newhook != NULL) 684 if ((error = (*node->type->newhook)(node, hook, name)) != 0) 685 goto fail; 686 687 /* 688 * The 'type' agrees so far, so go ahead and link it in. 689 * We'll ask again later when we actually connect the hooks. 690 */ 691 LIST_INSERT_HEAD(&node->hooks, hook, hooks); 692 node->numhooks++; 693 694 /* Set hook name */ 695 MALLOC(hook->name, char *, strlen(name) + 1, M_NETGRAPH, M_WAITOK); 696 if (hook->name == NULL) { 697 error = ENOMEM; 698 LIST_REMOVE(hook, hooks); 699 node->numhooks--; 700fail: 701 hook->node = NULL; 702 ng_unref(node); 703 ng_unref_hook(hook); /* this frees the hook */ 704 return (error); 705 } 706 strcpy(hook->name, name); 707 if (hookp) 708 *hookp = hook; 709 return (error); 710} 711 712/* 713 * Connect a pair of hooks. Only used internally. 714 */ 715static int 716ng_connect(hook_p hook1, hook_p hook2) 717{ 718 int error; 719 720 hook1->peer = hook2; 721 hook2->peer = hook1; 722 723 /* Give each node the opportunity to veto the impending connection */ 724 if (hook1->node->type->connect) { 725 if ((error = (*hook1->node->type->connect) (hook1))) { 726 ng_destroy_hook(hook1); /* also zaps hook2 */ 727 return (error); 728 } 729 } 730 if (hook2->node->type->connect) { 731 if ((error = (*hook2->node->type->connect) (hook2))) { 732 ng_destroy_hook(hook2); /* also zaps hook1 */ 733 return (error); 734 } 735 } 736 hook1->flags &= ~HK_INVALID; 737 hook2->flags &= ~HK_INVALID; 738 return (0); 739} 740 741/* 742 * Find a hook 743 * 744 * Node types may supply their own optimized routines for finding 745 * hooks. If none is supplied, we just do a linear search. 746 */ 747hook_p 748ng_findhook(node_p node, const char *name) 749{ 750 hook_p hook; 751 752 if (node->type->findhook != NULL) 753 return (*node->type->findhook)(node, name); 754 LIST_FOREACH(hook, &node->hooks, hooks) { 755 if (hook->name != NULL && strcmp(hook->name, name) == 0) 756 return (hook); 757 } 758 return (NULL); 759} 760 761/* 762 * Destroy a hook 763 * 764 * As hooks are always attached, this really destroys two hooks. 765 * The one given, and the one attached to it. Disconnect the hooks 766 * from each other first. 767 */ 768void 769ng_destroy_hook(hook_p hook) 770{ 771 hook_p peer = hook->peer; 772 773 hook->flags |= HK_INVALID; /* as soon as possible */ 774 if (peer) { 775 peer->flags |= HK_INVALID; /* as soon as possible */ 776 hook->peer = NULL; 777 peer->peer = NULL; 778 ng_disconnect_hook(peer); 779 } 780 ng_disconnect_hook(hook); 781} 782 783/* 784 * Notify the node of the hook's demise. This may result in more actions 785 * (e.g. shutdown) but we don't do that ourselves and don't know what 786 * happens there. If there is no appropriate handler, then just remove it 787 * (and decrement the reference count of it's node which in turn might 788 * make something happen). 789 */ 790static void 791ng_disconnect_hook(hook_p hook) 792{ 793 node_p node = hook->node; 794 795 /* 796 * Remove the hook from the node's list to avoid possible recursion 797 * in case the disconnection results in node shutdown. 798 */ 799 LIST_REMOVE(hook, hooks); 800 node->numhooks--; 801 if (node->type->disconnect) { 802 /* 803 * The type handler may elect to destroy the peer so don't 804 * trust its existance after this point. 805 */ 806 (*node->type->disconnect) (hook); 807 } 808 ng_unref(node); /* might be the last reference */ 809 if (hook->name) 810 FREE(hook->name, M_NETGRAPH); 811 hook->node = NULL; /* may still be referenced elsewhere */ 812 ng_unref_hook(hook); 813} 814 815/* 816 * Take two hooks on a node and merge the connection so that the given node 817 * is effectively bypassed. 818 */ 819int 820ng_bypass(hook_p hook1, hook_p hook2) 821{ 822 if (hook1->node != hook2->node) 823 return (EINVAL); 824 hook1->peer->peer = hook2->peer; 825 hook2->peer->peer = hook1->peer; 826 827 /* XXX If we ever cache methods on hooks update them as well */ 828 hook1->peer = NULL; 829 hook2->peer = NULL; 830 ng_destroy_hook(hook1); 831 ng_destroy_hook(hook2); 832 return (0); 833} 834 835/* 836 * Install a new netgraph type 837 */ 838int 839ng_newtype(struct ng_type *tp) 840{ 841 const size_t namelen = strlen(tp->name); 842 843 /* Check version and type name fields */ 844 if (tp->version != NG_VERSION || namelen == 0 || namelen > NG_TYPELEN) { 845 TRAP_ERROR; 846 return (EINVAL); 847 } 848 849 /* Check for name collision */ 850 if (ng_findtype(tp->name) != NULL) { 851 TRAP_ERROR; 852 return (EEXIST); 853 } 854 855 /* Link in new type */ 856 LIST_INSERT_HEAD(&typelist, tp, types); 857 tp->refs = 0; 858 return (0); 859} 860 861/* 862 * Look for a type of the name given 863 */ 864struct ng_type * 865ng_findtype(const char *typename) 866{ 867 struct ng_type *type; 868 869 LIST_FOREACH(type, &typelist, types) { 870 if (strcmp(type->name, typename) == 0) 871 break; 872 } 873 return (type); 874} 875 876 877/************************************************************************ 878 Composite routines 879************************************************************************/ 880 881/* 882 * Make a peer and connect. The order is arranged to minimise 883 * the work needed to back out in case of error. 884 */ 885int 886ng_mkpeer(node_p node, const char *name, const char *name2, char *type) 887{ 888 node_p node2; 889 hook_p hook; 890 hook_p hook2; 891 int error; 892 893 if ((error = ng_add_hook(node, name, &hook))) 894 return (error); 895 if ((error = ng_make_node(type, &node2))) { 896 ng_destroy_hook(hook); 897 return (error); 898 } 899 if ((error = ng_add_hook(node2, name2, &hook2))) { 900 ng_rmnode(node2); 901 ng_destroy_hook(hook); 902 return (error); 903 } 904 905 /* 906 * Actually link the two hooks together.. on failure they are 907 * destroyed so we don't have to do that here. 908 */ 909 if ((error = ng_connect(hook, hook2))) 910 ng_rmnode(node2); 911 return (error); 912} 913 914/* 915 * Connect two nodes using the specified hooks 916 */ 917int 918ng_con_nodes(node_p node, const char *name, node_p node2, const char *name2) 919{ 920 int error; 921 hook_p hook; 922 hook_p hook2; 923 924 if ((error = ng_add_hook(node, name, &hook))) 925 return (error); 926 if ((error = ng_add_hook(node2, name2, &hook2))) { 927 ng_destroy_hook(hook); 928 return (error); 929 } 930 return (ng_connect(hook, hook2)); 931} 932 933/* 934 * Parse and verify a string of the form: <NODE:><PATH> 935 * 936 * Such a string can refer to a specific node or a specific hook 937 * on a specific node, depending on how you look at it. In the 938 * latter case, the PATH component must not end in a dot. 939 * 940 * Both <NODE:> and <PATH> are optional. The <PATH> is a string 941 * of hook names separated by dots. This breaks out the original 942 * string, setting *nodep to "NODE" (or NULL if none) and *pathp 943 * to "PATH" (or NULL if degenerate). Also, *hookp will point to 944 * the final hook component of <PATH>, if any, otherwise NULL. 945 * 946 * This returns -1 if the path is malformed. The char ** are optional. 947 */ 948 949int 950ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) 951{ 952 char *node, *path, *hook; 953 int k; 954 955 /* 956 * Extract absolute NODE, if any 957 */ 958 for (path = addr; *path && *path != ':'; path++); 959 if (*path) { 960 node = addr; /* Here's the NODE */ 961 *path++ = '\0'; /* Here's the PATH */ 962 963 /* Node name must not be empty */ 964 if (!*node) 965 return -1; 966 967 /* A name of "." is OK; otherwise '.' not allowed */ 968 if (strcmp(node, ".") != 0) { 969 for (k = 0; node[k]; k++) 970 if (node[k] == '.') 971 return -1; 972 } 973 } else { 974 node = NULL; /* No absolute NODE */ 975 path = addr; /* Here's the PATH */ 976 } 977 978 /* Snoop for illegal characters in PATH */ 979 for (k = 0; path[k]; k++) 980 if (path[k] == ':') 981 return -1; 982 983 /* Check for no repeated dots in PATH */ 984 for (k = 0; path[k]; k++) 985 if (path[k] == '.' && path[k + 1] == '.') 986 return -1; 987 988 /* Remove extra (degenerate) dots from beginning or end of PATH */ 989 if (path[0] == '.') 990 path++; 991 if (*path && path[strlen(path) - 1] == '.') 992 path[strlen(path) - 1] = 0; 993 994 /* If PATH has a dot, then we're not talking about a hook */ 995 if (*path) { 996 for (hook = path, k = 0; path[k]; k++) 997 if (path[k] == '.') { 998 hook = NULL; 999 break; 1000 } 1001 } else 1002 path = hook = NULL; 1003 1004 /* Done */ 1005 if (nodep) 1006 *nodep = node; 1007 if (pathp) 1008 *pathp = path; 1009 if (hookp) 1010 *hookp = hook; 1011 return (0); 1012} 1013 1014/* 1015 * Given a path, which may be absolute or relative, and a starting node, 1016 * return the destination node. Compute the "return address" if desired. 1017 */ 1018int 1019ng_path2node(node_p here, const char *address, node_p *destp, char **rtnp) 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 1027 /* Initialize */ 1028 if (rtnp) 1029 *rtnp = NULL; 1030 if (destp == NULL) 1031 return EINVAL; 1032 *destp = NULL; 1033 1034 /* Make a writable copy of address for ng_path_parse() */ 1035 strncpy(fullpath, address, sizeof(fullpath) - 1); 1036 fullpath[sizeof(fullpath) - 1] = '\0'; 1037 1038 /* Parse out node and sequence of hooks */ 1039 if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { 1040 TRAP_ERROR; 1041 return EINVAL; 1042 } 1043 if (path == NULL) { 1044 pbuf[0] = '.'; /* Needs to be writable */ 1045 pbuf[1] = '\0'; 1046 path = pbuf; 1047 } 1048 1049 /* For an absolute address, jump to the starting node */ 1050 if (nodename) { 1051 node = ng_findname(here, nodename); 1052 if (node == NULL) { 1053 TRAP_ERROR; 1054 return (ENOENT); 1055 } 1056 } else 1057 node = here; 1058 1059 /* Now follow the sequence of hooks */ 1060 for (cp = path; node != NULL && *cp != '\0'; ) { 1061 hook_p hook; 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 return (0); 1117} 1118 1119/* 1120 * Call the appropriate message handler for the object. 1121 * It is up to the message handler to free the message. 1122 * If it's a generic message, handle it generically, otherwise 1123 * call the type's message handler (if it exists) 1124 */ 1125 1126#define CALL_MSG_HANDLER(error, node, msg, retaddr, resp) \ 1127do { \ 1128 if((msg)->header.typecookie == NGM_GENERIC_COOKIE) { \ 1129 (error) = ng_generic_msg((node), (msg), \ 1130 (retaddr), (resp)); \ 1131 } else { \ 1132 if ((node)->type->rcvmsg != NULL) { \ 1133 (error) = (*(node)->type->rcvmsg)((node), \ 1134 (msg), (retaddr), (resp)); \ 1135 } else { \ 1136 TRAP_ERROR; \ 1137 FREE((msg), M_NETGRAPH); \ 1138 (error) = EINVAL; \ 1139 } \ 1140 } \ 1141} while (0) 1142 1143 1144/* 1145 * Send a control message to a node 1146 */ 1147int 1148ng_send_msg(node_p here, struct ng_mesg *msg, const char *address, 1149 struct ng_mesg **rptr) 1150{ 1151 node_p dest = NULL; 1152 char *retaddr = NULL; 1153 int error; 1154 1155 /* Find the target node */ 1156 error = ng_path2node(here, address, &dest, &retaddr); 1157 if (error) { 1158 FREE(msg, M_NETGRAPH); 1159 return error; 1160 } 1161 1162 /* Make sure the resp field is null before we start */ 1163 if (rptr != NULL) 1164 *rptr = NULL; 1165 1166 CALL_MSG_HANDLER(error, dest, msg, retaddr, rptr); 1167 1168 /* Make sure that if there is a response, it has the RESP bit set */ 1169 if ((error == 0) && rptr && *rptr) 1170 (*rptr)->header.flags |= NGF_RESP; 1171 1172 /* 1173 * If we had a return address it is up to us to free it. They should 1174 * have taken a copy if they needed to make a delayed response. 1175 */ 1176 if (retaddr) 1177 FREE(retaddr, M_NETGRAPH); 1178 return (error); 1179} 1180 1181/* 1182 * Implement the 'generic' control messages 1183 */ 1184static int 1185ng_generic_msg(node_p here, struct ng_mesg *msg, const char *retaddr, 1186 struct ng_mesg **resp) 1187{ 1188 int error = 0; 1189 1190 if (msg->header.typecookie != NGM_GENERIC_COOKIE) { 1191 TRAP_ERROR; 1192 FREE(msg, M_NETGRAPH); 1193 return (EINVAL); 1194 } 1195 switch (msg->header.cmd) { 1196 case NGM_SHUTDOWN: 1197 ng_rmnode(here); 1198 break; 1199 case NGM_MKPEER: 1200 { 1201 struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; 1202 1203 if (msg->header.arglen != sizeof(*mkp)) { 1204 TRAP_ERROR; 1205 return (EINVAL); 1206 } 1207 mkp->type[sizeof(mkp->type) - 1] = '\0'; 1208 mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; 1209 mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; 1210 error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); 1211 break; 1212 } 1213 case NGM_CONNECT: 1214 { 1215 struct ngm_connect *const con = 1216 (struct ngm_connect *) msg->data; 1217 node_p node2; 1218 1219 if (msg->header.arglen != sizeof(*con)) { 1220 TRAP_ERROR; 1221 return (EINVAL); 1222 } 1223 con->path[sizeof(con->path) - 1] = '\0'; 1224 con->ourhook[sizeof(con->ourhook) - 1] = '\0'; 1225 con->peerhook[sizeof(con->peerhook) - 1] = '\0'; 1226 error = ng_path2node(here, con->path, &node2, NULL); 1227 if (error) 1228 break; 1229 error = ng_con_nodes(here, con->ourhook, node2, con->peerhook); 1230 break; 1231 } 1232 case NGM_NAME: 1233 { 1234 struct ngm_name *const nam = (struct ngm_name *) msg->data; 1235 1236 if (msg->header.arglen != sizeof(*nam)) { 1237 TRAP_ERROR; 1238 return (EINVAL); 1239 } 1240 nam->name[sizeof(nam->name) - 1] = '\0'; 1241 error = ng_name_node(here, nam->name); 1242 break; 1243 } 1244 case NGM_RMHOOK: 1245 { 1246 struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; 1247 hook_p hook; 1248 1249 if (msg->header.arglen != sizeof(*rmh)) { 1250 TRAP_ERROR; 1251 return (EINVAL); 1252 } 1253 rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; 1254 if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) 1255 ng_destroy_hook(hook); 1256 break; 1257 } 1258 case NGM_NODEINFO: 1259 { 1260 struct nodeinfo *ni; 1261 struct ng_mesg *rp; 1262 1263 /* Get response struct */ 1264 if (resp == NULL) { 1265 error = EINVAL; 1266 break; 1267 } 1268 NG_MKRESPONSE(rp, msg, sizeof(*ni), M_NOWAIT); 1269 if (rp == NULL) { 1270 error = ENOMEM; 1271 break; 1272 } 1273 1274 /* Fill in node info */ 1275 ni = (struct nodeinfo *) rp->data; 1276 if (here->name != NULL) 1277 strncpy(ni->name, here->name, NG_NODELEN); 1278 strncpy(ni->type, here->type->name, NG_TYPELEN); 1279 ni->id = ng_node2ID(here); 1280 ni->hooks = here->numhooks; 1281 *resp = rp; 1282 break; 1283 } 1284 case NGM_LISTHOOKS: 1285 { 1286 const int nhooks = here->numhooks; 1287 struct hooklist *hl; 1288 struct nodeinfo *ni; 1289 struct ng_mesg *rp; 1290 hook_p hook; 1291 1292 /* Get response struct */ 1293 if (resp == NULL) { 1294 error = EINVAL; 1295 break; 1296 } 1297 NG_MKRESPONSE(rp, msg, sizeof(*hl) 1298 + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); 1299 if (rp == NULL) { 1300 error = ENOMEM; 1301 break; 1302 } 1303 hl = (struct hooklist *) rp->data; 1304 ni = &hl->nodeinfo; 1305 1306 /* Fill in node info */ 1307 if (here->name) 1308 strncpy(ni->name, here->name, NG_NODELEN); 1309 strncpy(ni->type, here->type->name, NG_TYPELEN); 1310 ni->id = ng_node2ID(here); 1311 1312 /* Cycle through the linked list of hooks */ 1313 ni->hooks = 0; 1314 LIST_FOREACH(hook, &here->hooks, hooks) { 1315 struct linkinfo *const link = &hl->link[ni->hooks]; 1316 1317 if (ni->hooks >= nhooks) { 1318 log(LOG_ERR, "%s: number of %s changed\n", 1319 __FUNCTION__, "hooks"); 1320 break; 1321 } 1322 if ((hook->flags & HK_INVALID) != 0) 1323 continue; 1324 strncpy(link->ourhook, hook->name, NG_HOOKLEN); 1325 strncpy(link->peerhook, hook->peer->name, NG_HOOKLEN); 1326 if (hook->peer->node->name != NULL) 1327 strncpy(link->nodeinfo.name, 1328 hook->peer->node->name, NG_NODELEN); 1329 strncpy(link->nodeinfo.type, 1330 hook->peer->node->type->name, NG_TYPELEN); 1331 link->nodeinfo.id = ng_node2ID(hook->peer->node); 1332 link->nodeinfo.hooks = hook->peer->node->numhooks; 1333 ni->hooks++; 1334 } 1335 *resp = rp; 1336 break; 1337 } 1338 1339 case NGM_LISTNAMES: 1340 case NGM_LISTNODES: 1341 { 1342 const int unnamed = (msg->header.cmd == NGM_LISTNODES); 1343 struct namelist *nl; 1344 struct ng_mesg *rp; 1345 node_p node; 1346 int num = 0; 1347 1348 if (resp == NULL) { 1349 error = EINVAL; 1350 break; 1351 } 1352 1353 /* Count number of nodes */ 1354 LIST_FOREACH(node, &nodelist, nodes) { 1355 if (unnamed || node->name != NULL) 1356 num++; 1357 } 1358 1359 /* Get response struct */ 1360 if (resp == NULL) { 1361 error = EINVAL; 1362 break; 1363 } 1364 NG_MKRESPONSE(rp, msg, sizeof(*nl) 1365 + (num * sizeof(struct nodeinfo)), M_NOWAIT); 1366 if (rp == NULL) { 1367 error = ENOMEM; 1368 break; 1369 } 1370 nl = (struct namelist *) rp->data; 1371 1372 /* Cycle through the linked list of nodes */ 1373 nl->numnames = 0; 1374 LIST_FOREACH(node, &nodelist, nodes) { 1375 struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; 1376 1377 if (nl->numnames >= num) { 1378 log(LOG_ERR, "%s: number of %s changed\n", 1379 __FUNCTION__, "nodes"); 1380 break; 1381 } 1382 if ((node->flags & NG_INVALID) != 0) 1383 continue; 1384 if (!unnamed && node->name == NULL) 1385 continue; 1386 if (node->name != NULL) 1387 strncpy(np->name, node->name, NG_NODELEN); 1388 strncpy(np->type, node->type->name, NG_TYPELEN); 1389 np->id = ng_node2ID(node); 1390 np->hooks = node->numhooks; 1391 nl->numnames++; 1392 } 1393 *resp = rp; 1394 break; 1395 } 1396 1397 case NGM_LISTTYPES: 1398 { 1399 struct typelist *tl; 1400 struct ng_mesg *rp; 1401 struct ng_type *type; 1402 int num = 0; 1403 1404 if (resp == NULL) { 1405 error = EINVAL; 1406 break; 1407 } 1408 1409 /* Count number of types */ 1410 LIST_FOREACH(type, &typelist, types) 1411 num++; 1412 1413 /* Get response struct */ 1414 if (resp == NULL) { 1415 error = EINVAL; 1416 break; 1417 } 1418 NG_MKRESPONSE(rp, msg, sizeof(*tl) 1419 + (num * sizeof(struct typeinfo)), M_NOWAIT); 1420 if (rp == NULL) { 1421 error = ENOMEM; 1422 break; 1423 } 1424 tl = (struct typelist *) rp->data; 1425 1426 /* Cycle through the linked list of types */ 1427 tl->numtypes = 0; 1428 LIST_FOREACH(type, &typelist, types) { 1429 struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; 1430 1431 if (tl->numtypes >= num) { 1432 log(LOG_ERR, "%s: number of %s changed\n", 1433 __FUNCTION__, "types"); 1434 break; 1435 } 1436 strncpy(tp->typename, type->name, NG_TYPELEN); 1437 tp->numnodes = type->refs; 1438 tl->numtypes++; 1439 } 1440 *resp = rp; 1441 break; 1442 } 1443 1444 case NGM_BINARY2ASCII: 1445 { 1446 int bufSize = 2000; /* XXX hard coded constant */ 1447 const struct ng_parse_type *argstype; 1448 const struct ng_cmdlist *c; 1449 struct ng_mesg *rp, *binary, *ascii; 1450 1451 /* Data area must contain a valid netgraph message */ 1452 binary = (struct ng_mesg *)msg->data; 1453 if (msg->header.arglen < sizeof(struct ng_mesg) 1454 || msg->header.arglen - sizeof(struct ng_mesg) 1455 < binary->header.arglen) { 1456 error = EINVAL; 1457 break; 1458 } 1459 1460 /* Get a response message with lots of room */ 1461 NG_MKRESPONSE(rp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); 1462 if (rp == NULL) { 1463 error = ENOMEM; 1464 break; 1465 } 1466 ascii = (struct ng_mesg *)rp->data; 1467 1468 /* Copy binary message header to response message payload */ 1469 bcopy(binary, ascii, sizeof(*binary)); 1470 1471 /* Find command by matching typecookie and command number */ 1472 for (c = here->type->cmdlist; 1473 c != NULL && c->name != NULL; c++) { 1474 if (binary->header.typecookie == c->cookie 1475 && binary->header.cmd == c->cmd) 1476 break; 1477 } 1478 if (c == NULL || c->name == NULL) { 1479 for (c = ng_generic_cmds; c->name != NULL; c++) { 1480 if (binary->header.typecookie == c->cookie 1481 && binary->header.cmd == c->cmd) 1482 break; 1483 } 1484 if (c->name == NULL) { 1485 FREE(rp, M_NETGRAPH); 1486 error = ENOSYS; 1487 break; 1488 } 1489 } 1490 1491 /* Convert command name to ASCII */ 1492 snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), 1493 "%s", c->name); 1494 1495 /* Convert command arguments to ASCII */ 1496 argstype = (binary->header.flags & NGF_RESP) ? 1497 c->respType : c->mesgType; 1498 if (argstype == NULL) 1499 *ascii->data = '\0'; 1500 else { 1501 if ((error = ng_unparse(argstype, 1502 (u_char *)binary->data, 1503 ascii->data, bufSize)) != 0) { 1504 FREE(rp, M_NETGRAPH); 1505 break; 1506 } 1507 } 1508 1509 /* Return the result as struct ng_mesg plus ASCII string */ 1510 bufSize = strlen(ascii->data) + 1; 1511 ascii->header.arglen = bufSize; 1512 rp->header.arglen = sizeof(*ascii) + bufSize; 1513 *resp = rp; 1514 break; 1515 } 1516 1517 case NGM_ASCII2BINARY: 1518 { 1519 int bufSize = 2000; /* XXX hard coded constant */ 1520 const struct ng_cmdlist *c; 1521 const struct ng_parse_type *argstype; 1522 struct ng_mesg *rp, *ascii, *binary; 1523 int off; 1524 1525 /* Data area must contain at least a struct ng_mesg + '\0' */ 1526 ascii = (struct ng_mesg *)msg->data; 1527 if (msg->header.arglen < sizeof(*ascii) + 1 1528 || ascii->header.arglen < 1 1529 || msg->header.arglen 1530 < sizeof(*ascii) + ascii->header.arglen) { 1531 error = EINVAL; 1532 break; 1533 } 1534 ascii->data[ascii->header.arglen - 1] = '\0'; 1535 1536 /* Get a response message with lots of room */ 1537 NG_MKRESPONSE(rp, msg, sizeof(*binary) + bufSize, M_NOWAIT); 1538 if (rp == NULL) { 1539 error = ENOMEM; 1540 break; 1541 } 1542 binary = (struct ng_mesg *)rp->data; 1543 1544 /* Copy ASCII message header to response message payload */ 1545 bcopy(ascii, binary, sizeof(*ascii)); 1546 1547 /* Find command by matching ASCII command string */ 1548 for (c = here->type->cmdlist; 1549 c != NULL && c->name != NULL; c++) { 1550 if (strcmp(ascii->header.cmdstr, c->name) == 0) 1551 break; 1552 } 1553 if (c == NULL || c->name == NULL) { 1554 for (c = ng_generic_cmds; c->name != NULL; c++) { 1555 if (strcmp(ascii->header.cmdstr, c->name) == 0) 1556 break; 1557 } 1558 if (c->name == NULL) { 1559 FREE(rp, M_NETGRAPH); 1560 error = ENOSYS; 1561 break; 1562 } 1563 } 1564 1565 /* Convert command name to binary */ 1566 binary->header.cmd = c->cmd; 1567 binary->header.typecookie = c->cookie; 1568 1569 /* Convert command arguments to binary */ 1570 argstype = (binary->header.flags & NGF_RESP) ? 1571 c->respType : c->mesgType; 1572 if (argstype == NULL) 1573 bufSize = 0; 1574 else { 1575 if ((error = ng_parse(argstype, ascii->data, 1576 &off, (u_char *)binary->data, &bufSize)) != 0) { 1577 FREE(rp, M_NETGRAPH); 1578 break; 1579 } 1580 } 1581 1582 /* Return the result */ 1583 binary->header.arglen = bufSize; 1584 rp->header.arglen = sizeof(*binary) + bufSize; 1585 *resp = rp; 1586 break; 1587 } 1588 1589 case NGM_TEXT_STATUS: 1590 /* 1591 * This one is tricky as it passes the command down to the 1592 * actual node, even though it is a generic type command. 1593 * This means we must assume that the msg is already freed 1594 * when control passes back to us. 1595 */ 1596 if (resp == NULL) { 1597 error = EINVAL; 1598 break; 1599 } 1600 if (here->type->rcvmsg != NULL) 1601 return((*here->type->rcvmsg)(here, msg, retaddr, resp)); 1602 /* Fall through if rcvmsg not supported */ 1603 default: 1604 TRAP_ERROR; 1605 error = EINVAL; 1606 } 1607 FREE(msg, M_NETGRAPH); 1608 return (error); 1609} 1610 1611/* 1612 * Send a data packet to a node. If the recipient has no 1613 * 'receive data' method, then silently discard the packet. 1614 */ 1615int 1616ng_send_data(hook_p hook, struct mbuf *m, meta_p meta) 1617{ 1618 int (*rcvdata)(hook_p, struct mbuf *, meta_p); 1619 int error; 1620 1621 CHECK_DATA_MBUF(m); 1622 if (hook && (hook->flags & HK_INVALID) == 0) { 1623 rcvdata = hook->peer->node->type->rcvdata; 1624 if (rcvdata != NULL) 1625 error = (*rcvdata)(hook->peer, m, meta); 1626 else { 1627 error = 0; 1628 NG_FREE_DATA(m, meta); 1629 } 1630 } else { 1631 TRAP_ERROR; 1632 error = ENOTCONN; 1633 NG_FREE_DATA(m, meta); 1634 } 1635 return (error); 1636} 1637 1638/* 1639 * Send a queued data packet to a node. If the recipient has no 1640 * 'receive queued data' method, then try the 'receive data' method above. 1641 */ 1642int 1643ng_send_dataq(hook_p hook, struct mbuf *m, meta_p meta) 1644{ 1645 int (*rcvdataq)(hook_p, struct mbuf *, meta_p); 1646 int error; 1647 1648 CHECK_DATA_MBUF(m); 1649 if (hook && (hook->flags & HK_INVALID) == 0) { 1650 rcvdataq = hook->peer->node->type->rcvdataq; 1651 if (rcvdataq != NULL) 1652 error = (*rcvdataq)(hook->peer, m, meta); 1653 else { 1654 error = ng_send_data(hook, m, meta); 1655 } 1656 } else { 1657 TRAP_ERROR; 1658 error = ENOTCONN; 1659 NG_FREE_DATA(m, meta); 1660 } 1661 return (error); 1662} 1663 1664/************************************************************************ 1665 Module routines 1666************************************************************************/ 1667 1668/* 1669 * Handle the loading/unloading of a netgraph node type module 1670 */ 1671int 1672ng_mod_event(module_t mod, int event, void *data) 1673{ 1674 struct ng_type *const type = data; 1675 int s, error = 0; 1676 1677 switch (event) { 1678 case MOD_LOAD: 1679 1680 /* Register new netgraph node type */ 1681 s = splnet(); 1682 if ((error = ng_newtype(type)) != 0) { 1683 splx(s); 1684 break; 1685 } 1686 1687 /* Call type specific code */ 1688 if (type->mod_event != NULL) 1689 if ((error = (*type->mod_event)(mod, event, data)) != 0) 1690 LIST_REMOVE(type, types); 1691 splx(s); 1692 break; 1693 1694 case MOD_UNLOAD: 1695 s = splnet(); 1696 if (type->refs != 0) /* make sure no nodes exist! */ 1697 error = EBUSY; 1698 else { 1699 if (type->mod_event != NULL) { /* check with type */ 1700 error = (*type->mod_event)(mod, event, data); 1701 if (error != 0) { /* type refuses.. */ 1702 splx(s); 1703 break; 1704 } 1705 } 1706 LIST_REMOVE(type, types); 1707 } 1708 splx(s); 1709 break; 1710 1711 default: 1712 if (type->mod_event != NULL) 1713 error = (*type->mod_event)(mod, event, data); 1714 else 1715 error = 0; /* XXX ? */ 1716 break; 1717 } 1718 return (error); 1719} 1720 1721/* 1722 * Handle loading and unloading for this code. 1723 * The only thing we need to link into is the NETISR strucure. 1724 */ 1725static int 1726ngb_mod_event(module_t mod, int event, void *data) 1727{ 1728 int s, error = 0; 1729 1730 switch (event) { 1731 case MOD_LOAD: 1732 /* Register line discipline */ 1733 s = splimp(); 1734 error = register_netisr(NETISR_NETGRAPH, ngintr); 1735 splx(s); 1736 break; 1737 case MOD_UNLOAD: 1738 /* You cant unload it because an interface may be using it. */ 1739 error = EBUSY; 1740 break; 1741 default: 1742 error = EOPNOTSUPP; 1743 break; 1744 } 1745 return (error); 1746} 1747 1748static moduledata_t netgraph_mod = { 1749 "netgraph", 1750 ngb_mod_event, 1751 (NULL) 1752}; 1753DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 1754 1755/************************************************************************ 1756 Queueing routines 1757************************************************************************/ 1758 1759/* The structure for queueing across ISR switches */ 1760struct ng_queue_entry { 1761 u_long flags; 1762 struct ng_queue_entry *next; 1763 union { 1764 struct { 1765 hook_p da_hook; /* target hook */ 1766 struct mbuf *da_m; 1767 meta_p da_meta; 1768 } data; 1769 struct { 1770 struct ng_mesg *msg_msg; 1771 node_p msg_node; 1772 void *msg_retaddr; 1773 } msg; 1774 } body; 1775}; 1776#define NGQF_DATA 0x01 /* the queue element is data */ 1777#define NGQF_MESG 0x02 /* the queue element is a message */ 1778 1779static struct ng_queue_entry *ngqbase; /* items to be unqueued */ 1780static struct ng_queue_entry *ngqlast; /* last item queued */ 1781static const int ngqroom = 64; /* max items to queue */ 1782static int ngqsize; /* number of items in queue */ 1783 1784static struct ng_queue_entry *ngqfree; /* free ones */ 1785static const int ngqfreemax = 16;/* cache at most this many */ 1786static int ngqfreesize; /* number of cached entries */ 1787 1788/* 1789 * Get a queue entry 1790 */ 1791static struct ng_queue_entry * 1792ng_getqblk(void) 1793{ 1794 register struct ng_queue_entry *q; 1795 int s; 1796 1797 /* Could be guarding against tty ints or whatever */ 1798 s = splhigh(); 1799 1800 /* Try get a cached queue block, or else allocate a new one */ 1801 if ((q = ngqfree) == NULL) { 1802 splx(s); 1803 if (ngqsize < ngqroom) { /* don't worry about races */ 1804 MALLOC(q, struct ng_queue_entry *, 1805 sizeof(*q), M_NETGRAPH, M_NOWAIT); 1806 } 1807 } else { 1808 ngqfree = q->next; 1809 ngqfreesize--; 1810 splx(s); 1811 } 1812 return (q); 1813} 1814 1815/* 1816 * Release a queue entry 1817 */ 1818#define RETURN_QBLK(q) \ 1819do { \ 1820 int s; \ 1821 if (ngqfreesize < ngqfreemax) { /* don't worry about races */ \ 1822 s = splhigh(); \ 1823 (q)->next = ngqfree; \ 1824 ngqfree = (q); \ 1825 ngqfreesize++; \ 1826 splx(s); \ 1827 } else { \ 1828 FREE((q), M_NETGRAPH); \ 1829 } \ 1830} while (0) 1831 1832/* 1833 * Running at a raised (but we don't know which) processor priority level, 1834 * put the data onto a queue to be picked up by another PPL (probably splnet) 1835 */ 1836int 1837ng_queue_data(hook_p hook, struct mbuf *m, meta_p meta) 1838{ 1839 struct ng_queue_entry *q; 1840 int s; 1841 1842 if (hook == NULL) { 1843 NG_FREE_DATA(m, meta); 1844 return (0); 1845 } 1846 if ((q = ng_getqblk()) == NULL) { 1847 NG_FREE_DATA(m, meta); 1848 return (ENOBUFS); 1849 } 1850 1851 /* Fill out the contents */ 1852 q->flags = NGQF_DATA; 1853 q->next = NULL; 1854 q->body.data.da_hook = hook; 1855 q->body.data.da_m = m; 1856 q->body.data.da_meta = meta; 1857 hook->refs++; /* don't let it go away while on the queue */ 1858 1859 /* Put it on the queue */ 1860 s = splhigh(); 1861 if (ngqbase) { 1862 ngqlast->next = q; 1863 } else { 1864 ngqbase = q; 1865 } 1866 ngqlast = q; 1867 ngqsize++; 1868 splx(s); 1869 1870 /* Schedule software interrupt to handle it later */ 1871 schednetisr(NETISR_NETGRAPH); 1872 return (0); 1873} 1874 1875/* 1876 * Running at a raised (but we don't know which) processor priority level, 1877 * put the msg onto a queue to be picked up by another PPL (probably splnet) 1878 */ 1879int 1880ng_queue_msg(node_p here, struct ng_mesg * msg, int len, const char *address) 1881{ 1882 register struct ng_queue_entry *q; 1883 int s; 1884 node_p dest = NULL; 1885 char *retaddr = NULL; 1886 int error; 1887 1888 /* Find the target node. */ 1889 error = ng_path2node(here, address, &dest, &retaddr); 1890 if (error) { 1891 FREE(msg, M_NETGRAPH); 1892 return (error); 1893 } 1894 if ((q = ng_getqblk()) == NULL) { 1895 FREE(msg, M_NETGRAPH); 1896 if (retaddr) 1897 FREE(retaddr, M_NETGRAPH); 1898 return (ENOBUFS); 1899 } 1900 1901 /* Fill out the contents */ 1902 q->flags = NGQF_MESG; 1903 q->next = NULL; 1904 q->body.msg.msg_node = dest; 1905 q->body.msg.msg_msg = msg; 1906 q->body.msg.msg_retaddr = retaddr; 1907 dest->refs++; /* don't let it go away while on the queue */ 1908 1909 /* Put it on the queue */ 1910 s = splhigh(); 1911 if (ngqbase) { 1912 ngqlast->next = q; 1913 } else { 1914 ngqbase = q; 1915 } 1916 ngqlast = q; 1917 ngqsize++; 1918 splx(s); 1919 1920 /* Schedule software interrupt to handle it later */ 1921 schednetisr(NETISR_NETGRAPH); 1922 return (0); 1923} 1924 1925/* 1926 * Pick an item off the queue, process it, and dispose of the queue entry. 1927 * Should be running at splnet. 1928 */ 1929static void 1930ngintr(void) 1931{ 1932 hook_p hook; 1933 struct ng_queue_entry *ngq; 1934 struct mbuf *m; 1935 meta_p meta; 1936 void *retaddr; 1937 struct ng_mesg *msg; 1938 node_p node; 1939 int error = 0; 1940 int s; 1941 1942 while (1) { 1943 s = splhigh(); 1944 if ((ngq = ngqbase)) { 1945 ngqbase = ngq->next; 1946 ngqsize--; 1947 } 1948 splx(s); 1949 if (ngq == NULL) 1950 return; 1951 switch (ngq->flags) { 1952 case NGQF_DATA: 1953 hook = ngq->body.data.da_hook; 1954 m = ngq->body.data.da_m; 1955 meta = ngq->body.data.da_meta; 1956 RETURN_QBLK(ngq); 1957 NG_SEND_DATAQ(error, hook, m, meta); 1958 ng_unref_hook(hook); 1959 break; 1960 case NGQF_MESG: 1961 node = ngq->body.msg.msg_node; 1962 msg = ngq->body.msg.msg_msg; 1963 retaddr = ngq->body.msg.msg_retaddr; 1964 RETURN_QBLK(ngq); 1965 if (node->flags & NG_INVALID) { 1966 FREE(msg, M_NETGRAPH); 1967 } else { 1968 CALL_MSG_HANDLER(error, node, msg, 1969 retaddr, NULL); 1970 } 1971 ng_unref(node); 1972 if (retaddr) 1973 FREE(retaddr, M_NETGRAPH); 1974 break; 1975 default: 1976 RETURN_QBLK(ngq); 1977 } 1978 } 1979} 1980 1981 1982