|
|
1.1 ! root 1: .\" smux-api.rf - How to export... ! 2: .de UL ! 3: .ti -.5i ! 4: ------- ! 5: .br ! 6: .. ! 7: .ie \nh \{\ ! 8: .ll 66 ! 9: .po 2 \} ! 10: .el \{\ ! 11: .ll 62 ! 12: .pl 58 ! 13: .nr sf R ! 14: .nr fm 0 \} ! 15: .he 'draft'Exporting MIBs for BSD UNIX'May 1990' ! 16: .fo 'M.T. Rose''[Page %]' ! 17: .de $0 ! 18: .(x t ! 19: \\$2 \\$1 ! 20: .)x ! 21: .. ! 22: .lp ! 23: .na ! 24: .nh ! 25: .ce 3 ! 26: \fBHow to export a MIB module ! 27: from a BSD UNIX daemon ! 28: using the 4BSD/ISODE SMUX API\fR ! 29: .sp 1 ! 30: .ce ! 31: Sun May 13 14:54:13 1990 ! 32: .sp 2 ! 33: .ce ! 34: Marshall T. Rose ! 35: .sp 1 ! 36: .ce 4 ! 37: Performance Systems International, Inc. ! 38: 11800 Sunrise Valley Drive ! 39: Suite 1100 ! 40: Reston, VA 22091 ! 41: .sp 1 ! 42: .ce ! 43: [email protected] ! 44: .sp 5 ! 45: .sh 1 "Status of this Memo" ! 46: .lp ! 47: This document defines an API for UNIX daemons wishing to implement a ! 48: MIB module on a BSD UNIX system using the 4BSD/ISODE SNMP software. ! 49: This is a local mechanism. ! 50: ! 51: .bp ! 52: .sh 1 "The Environment" ! 53: .lp ! 54: This document gives an overview of how one modifies a UNIX program ! 55: to export a MIB module to the local SNMP agent. ! 56: .lp ! 57: All of the files necessary to interface to the SNMP agent is contained ! 58: in the ISODE source tree, ! 59: in the \fBsnmp/\fR directory. ! 60: Since this document avoids giving the actual \fIC\fR procedural ! 61: definitions, ! 62: you should familiarize yourself with the \fIlint\fR library, \fBllib-lisnmp\fR. ! 63: .lp ! 64: For the purposes of example, ! 65: throughout this document we will reference a simple UNIX daemon, ! 66: \fIunixd\fR, ! 67: which implements a MIB module for \*(lqmbuf statistics\*(rq. ! 68: ! 69: .sh 2 "The ISODE" ! 70: .lp ! 71: As you might have guessed, ! 72: this document assumes that you're running the ISODE on your system. ! 73: You should read the \fBREAD-ME\fR file at the base of the source tree ! 74: for instructions on how to configure, generate, and install the ISODE. ! 75: In the future, an abbreviated set of instructions, ! 76: for those interested in running the only 4BSD/ISODE SNMP software, ! 77: will be written. ! 78: .lp ! 79: For now, ! 80: follow the \fBREAD-ME\fR, ! 81: generate the base system and SNMP, e.g., ! 82: .sp ! 83: .in +.5i ! 84: .nf ! 85: % ./make all all-snmp ! 86: .fi ! 87: .in -.5i ! 88: .sp ! 89: and then install only the 4BSD/ISODE SNMP software: ! 90: .sp ! 91: .in +.5i ! 92: .nf ! 93: # ./make inst-all inst-snmp ! 94: .fi ! 95: .in -.5i ! 96: .sp ! 97: However, ! 98: be sure to read the entire \fBREAD-ME\fR file, ! 99: as there are other steps involved in installing the SNMP software. ! 100: ! 101: .bp ! 102: .sh 1 "MIB Modules" ! 103: .lp ! 104: The first thing you need to do is to define the actual managed objects ! 105: which your program will implement. ! 106: This is done by writing a \*(lqMIB module\*(rq. ! 107: The syntax of the module is defined in the Internet-standard SMI, RFC1155. ! 108: It is not the purpose of this document to describe the rules used for ! 109: writing a MIB module. ! 110: However, here are the basics to speed you on your way. ! 111: .lp ! 112: The MIB module is defined in a file called \fImodule\fR.my, ! 113: e.g., \fBunix.my\fR. ! 114: In general, ! 115: there are three kinds of MIB modules: ! 116: .np ! 117: If you are defining a MIB module for something UNIX specific, ! 118: it should probably go under the BSD UNIX MIB ! 119: (contact Marshall Rose or Keith Sklower to get a number under the UNIX ! 120: enterprise tree). ! 121: In this case, ! 122: the definition might start something like this: ! 123: .sp ! 124: .in +.5i ! 125: .nf ! 126: SendMail-MIB { iso org(3) dod(6) internet(1) private(4) ! 127: enterprises(1) unix (4) sendmail (99) } ! 128: ! 129: DEFINITIONS ::= BEGIN ! 130: ! 131: IMPORTS ! 132: unix --*, OBJECT-TYPE *-- ! 133: FROM RFC1155-SMI; ! 134: ! 135: sendMail OBJECT IDENTIFIER ::= { unix 99 } ! 136: .fi ! 137: .in -.5i ! 138: .sp ! 139: .np ! 140: If you are defining a MIB module on a multilateral, experimental basis, ! 141: e.g., for a protocol like the NTP, ! 142: then you should contact the Internet Assigned Numbers Authority ([email protected]) ! 143: and ask for an experimental number. ! 144: In this case, ! 145: the definition might start something like this: ! 146: .sp ! 147: .in +.5i ! 148: .nf ! 149: FIZBIN-MIB DEFINITIONS ::= BEGIN ! 150: ! 151: IMPORTS ! 152: experimental, OBJECT-TYPE ! 153: FROM RFC1155-SMI; ! 154: ! 155: fizBin OBJECT IDENTIFIER ::= { experimental 99 } ! 156: .fi ! 157: .in -.5i ! 158: .sp ! 159: .np ! 160: Otherwise, ! 161: if you are defining a MIB module for something specific to your ! 162: enterprise, ! 163: then you contact the Internet Assigned Numbers Authority ([email protected]) and ! 164: ask for an enterprise number (assuming you don't already have one). ! 165: In this case, ! 166: the definition might start something like this: ! 167: .sp ! 168: .in +.5i ! 169: .nf ! 170: FIZZBIN-MIB DEFINITIONS ::= BEGIN ! 171: ! 172: IMPORTS ! 173: enterprises, OBJECT-TYPE ! 174: FROM RFC1155-SMI; ! 175: ! 176: cheetah OBJECT IDENTIFIER ::= { enterprises 9999 } ! 177: ! 178: fizBin OBJECT IDENTIFIER ::= { cheetah 1 } ! 179: .fi ! 180: .in -.5i ! 181: .sp ! 182: .lp ! 183: Regardless of the kind of MIB module, ! 184: this last OBJECT IDENTIFIER points to the root of the tree which your ! 185: agent is going to export. ! 186: .lp ! 187: Following this start, ! 188: you have the actual definitions of the MIB objects, ! 189: followed by ! 190: .sp ! 191: .in +.5i ! 192: .nf ! 193: END ! 194: .fi ! 195: .in -.5i ! 196: .sp ! 197: ! 198: .sh 2 "Compiling MIB modules" ! 199: .lp ! 200: The next step is to compile the MIB module into a form that your ! 201: program can read. ! 202: This is done using the \fImosy\fR program: ! 203: .sp ! 204: .in +.5i ! 205: .nf ! 206: % others/mosy/xmosy \fImodule\fR.my ! 207: .fi ! 208: .in -.5i ! 209: .sp ! 210: which will create the file \fImodule\fR.defs. ! 211: In most cases you will need to prefix this file with definitions from ! 212: the root of the OBJECT IDENTIFIER tree, ! 213: e.g., ! 214: .sp ! 215: .in +.5i ! 216: .nf ! 217: % cat $(INCDIR)isode/snmp/smi.defs \fImodule\fR.defs > \fIdaemon\fR.defs ! 218: .fi ! 219: .in -.5i ! 220: .sp ! 221: where the \fBdaemon.defs\fR file will be the one which you install with ! 222: your program binary. ! 223: ! 224: .sh 3 "The Syntax of compiled MIB modules" ! 225: .lp ! 226: The syntax is pretty simple: ! 227: .np ! 228: Comments start with \*(lq--\*(rq or \*(lq#\*(rq at the beginning of a line. ! 229: .np ! 230: Object identifiers are defined like this: ! 231: .sp ! 232: .in +.5i ! 233: .nf ! 234: name value ! 235: .fi ! 236: .in -.5i ! 237: .sp ! 238: e.g., ! 239: .sp ! 240: .in +.5i ! 241: .nf ! 242: internet iso.3.6.1 ! 243: .fi ! 244: .in -.5i ! 245: .sp ! 246: .np ! 247: Object types are defined like this: ! 248: .sp ! 249: .in +.5i ! 250: .nf ! 251: name oid syntax access status ! 252: .fi ! 253: .in -.5i ! 254: .sp ! 255: e.g., ! 256: .sp ! 257: .in +.5i ! 258: .nf ! 259: sysDescr system.1 DisplayString read-only mandatory ! 260: .fi ! 261: .in -.5i ! 262: .sp ! 263: where \*(lqname\*(rq and \*(lqoid\*(rq are fairly obvious. ! 264: For the rest: ! 265: .sp ! 266: .in +.5i ! 267: .nf ! 268: \*(lqsyntax\*(rq is the name of a defined syntax; ! 269: .sp ! 270: \*(lqaccess\*(rq is one of \fIread-only\fR, \fIread-write\fR", or \fInone\fR; ! 271: and, ! 272: .sp ! 273: \*(lqstatus\*(rq is one of \fImandatory\fR, \fIoptional\fR, ! 274: \fIdeprecated\fR, or \fIobsolete\fR. ! 275: .fi ! 276: .in -.5i ! 277: .sp ! 278: Names of objects are \fIalways\fR case-sensitive. ! 279: ! 280: .bp ! 281: .sh 1 "SMUX Peers" ! 282: .lp ! 283: A program which exports a MIB module is termed a \*(lqSMUX peer\*(rq ! 284: (SMUX is the name of the protocol used by these programs to ! 285: communicate with an SNMP agent.) ! 286: .lp ! 287: There is a textual database, ! 288: \fB$(ETCDIR)snmpd.peers\fR, ! 289: which defines the SMUX peers known to the local SNMP agent. ! 290: The syntax of this file is pretty simple: ! 291: .np ! 292: Comments start with \*(lq#\*(rq at the beginning of a line. ! 293: .np ! 294: Each peer is identified in a single line: ! 295: .sp ! 296: .in +.5i ! 297: .nf ! 298: name oid password [priority] ! 299: .fi ! 300: .in -.5i ! 301: .sp ! 302: where \*(lqname\*(rq and \*(lqoid\*(rq are fairly obvious. ! 303: For the rest: ! 304: .sp ! 305: .in +.5i ! 306: .nf ! 307: \*(lqpassword\*(rq is a string which the agent will use to ! 308: authenticate the SMUX peer; ! 309: and, ! 310: .sp ! 311: \*(lqpriority\*(rq, ! 312: if present, ! 313: is the highest priority with which the SMUX peer can register subtrees. ! 314: .fi ! 315: .in -.5i ! 316: .sp ! 317: The name/oid pairs are assigned by the authority for the BSD UNIX MIB ! 318: (contact Marshall Rose or Keith Sklower to register the name of your ! 319: program and get an for your program). ! 320: .lp ! 321: Note that this file contains things resembling passwords in the clear. ! 322: As such, it should be protected mode 0600 and owned by root. ! 323: ! 324: .bp ! 325: .sh 1 "Daemon Skeleton" ! 326: .lp ! 327: Now it's time to modify your daemon to export the MIB. ! 328: Your source code should include these lines: ! 329: .sp ! 330: .in +.5i ! 331: .nf ! 332: #include <isode/snmp/smux.h> ! 333: #include <isode/snmp/objects.h> ! 334: #include <isode/tailor.h> ! 335: .fi ! 336: .in -.5i ! 337: .sp ! 338: which will include the definitions for: ! 339: talking to the SNMP agent via the SMUX protocol, ! 340: the object management package, ! 341: and the ISODE tailoring subsystem. ! 342: .lp ! 343: When loading your daemon, ! 344: you should add this to the end of your command to the loader: ! 345: .sp ! 346: .in +.5i ! 347: .nf ! 348: -lisnmp -lisode ! 349: .fi ! 350: .in -.5i ! 351: .sp ! 352: which includes the 4BSD/SNMP and ISODE libraries. ! 353: .lp ! 354: You should decide if you want to use the ISODE logging subsystem. ! 355: This is a convenient mechanism for using a unified logging system. ! 356: If you are modifying an already existing daemon, ! 357: you probably have your own logging package. ! 358: Otherwise, ! 359: you should consider using the ISODE subsystem. ! 360: ! 361: .sh 2 "Declarations" ! 362: .lp ! 363: There are some global variables that your program will have to declare: ! 364: .sp ! 365: .in +.5i ! 366: .nf ! 367: int debug = 0; ! 368: static char *myname = "unixd"; ! 369: ! 370: static LLog _pgm_log = { ! 371: "unixd.log", NULLCP, NULLCP, ! 372: LLOG_FATAL | LLOG_EXCEPTIONS | LLOG_NOTICE, ! 373: LLOG_FATAL, -1, LLOGCLS | LLOGCRT | LLOGZER, NOTOK ! 374: }; ! 375: static LLog *pgm_log = &_pgm_log; ! 376: ! 377: static OID subtree = NULLOID; ! 378: static struct smuxEntry *se = NULL; ! 379: ! 380: static int smux_fd = NOTOK; ! 381: static int rock_and_roll = 0; ! 382: static int dont_bother_anymore = 0; ! 383: ! 384: static int quantum = 0; ! 385: .fi ! 386: .in -.5i ! 387: .sp ! 388: You only need the definition of \fI_pgm_log\fR and \fIpgm_log\fR if ! 389: you will be using the ISODE logging subsystem. ! 390: ! 391: .sh 2 "Initialization" ! 392: .lp ! 393: When your program initializes itself, ! 394: it should contain some code like this: ! 395: .sp ! 396: .in +.5i ! 397: .nf ! 398: if (myname = rindex (argv[0], '/')) ! 399: myname++; ! 400: if (myname == NULL || *myname == NULL) ! 401: myname = argv[0]; ! 402: ! 403: isodetailor (myname, 0); ! 404: ll_hdinit (pgm_log, myname); ! 405: ! 406: /* scan argv, set debug if need be... */ ! 407: ! 408: if (debug) ! 409: ll_dbinit (pgm_log, myname); ! 410: .fi ! 411: .in -.5i ! 412: .sp ! 413: Of course, ! 414: if you're not using the ISODE logging subsystem, ! 415: you don't have the calls to \fIll_hdinit\fR or \fIll_dbinit\fR. ! 416: .lp ! 417: After cracking argv, ! 418: your program should do the usual detaching actions. ! 419: Usually at this point, ! 420: your program reads the compiled MIB module definitions and starts ! 421: talking to the SNMP agent. ! 422: ! 423: .sh 2 "Reading the compiled MIB module" ! 424: .lp ! 425: You program should call the \fIreadobjects\fR routine to read the module, ! 426: look-up the subtree which it will export to the SNMP agent, ! 427: and call the \fIgetsmuxEntrybyname\fR routine to find its entry in the ! 428: \fBsnmpd.peers\fR database. ! 429: Finally, ! 430: it should call a routine you define, ! 431: e.g., \fIinit_mib\fR, ! 432: to initialize it's internal MIB structures. ! 433: (We'll look at this routine in greater detail later on.) ! 434: .sp ! 435: .in +.5i ! 436: .nf ! 437: OT ot; ! 438: ! 439: if (readobjects ("unixd.defs") == NOTOK) ! 440: error ("readobjects: %s", PY_pepy); ! 441: ! 442: if ((ot = text2obj ("mbuf")) == NULL) ! 443: error ("object \"%s\" not in \"%s\"", "mbuf", "unix.defs"); ! 444: subtree = ot -> ot_name; ! 445: ! 446: if (se = getsmuxEntrybyname ("unixd")) == NULL) ! 447: error ("no SMUX entry for \"%s\"", "unixd"); ! 448: ! 449: init_mib (); ! 450: .fi ! 451: .in -.5i ! 452: .sp ! 453: If any of these routines fail, ! 454: then don't bother trying to export the MIB module. ! 455: ! 456: .sh 2 "Talking to the SNMP agent" ! 457: .lp ! 458: All of the SMUX routines return \fINOTOK\fR on failure. ! 459: Unless otherwise noted, ! 460: these routines also return \fIOK\fR on success. ! 461: On failure, the variable ! 462: .sp ! 463: .in +.5i ! 464: .nf ! 465: extern int smux_errno; ! 466: .fi ! 467: .in -.5i ! 468: .sp ! 469: is set to a symbolic value defined in \fBsmux.h\fR, one of: ! 470: .sp ! 471: .in +.5i ! 472: .nf ! 473: invalidOperation ! 474: parameterMissing ! 475: systemError ! 476: youLoseBig ! 477: congestion ! 478: inProgress ! 479: .fi ! 480: .in -.5i ! 481: .sp ! 482: In addition, the variable ! 483: .sp ! 484: .in +.5i ! 485: .nf ! 486: extern char smux_info[BUFSIZ]; ! 487: .fi ! 488: .in -.5i ! 489: .sp ! 490: contains a printable explanation of what happened on failure. ! 491: .lp ! 492: All errors are FATAL except for \fIinProgress\fR. ! 493: This means retry your operation later on. ! 494: ! 495: .sh 3 "Initialization" ! 496: .lp ! 497: You program should call the routine \fIsmux_init\fR, ! 498: which initializes a SMUX connection to the local SNMP agent. ! 499: This starts a TCP connection, ! 500: but most likely does not complete it. ! 501: You program will need to call an open routine (there is only one at present) ! 502: to finish the connect and establish the SMUX association. ! 503: .lp ! 504: If successful, ! 505: the return value is a file-descriptor, ! 506: suitable for use with \fIselect\fR, etc. ! 507: .lp ! 508: On failure, ! 509: \fIsmux_errno\fR will be set to one of ! 510: \fIcongestion\fR, \fIyouLoseBig\fR, or \fIsystemError\fR. ! 511: In this case, ! 512: you should probably have your program retry the operation every 5 ! 513: minutes or so. ! 514: .sp ! 515: .in +.5i ! 516: .nf ! 517: if ((smux_fd = smux_init (debug)) == NOTOK) ! 518: error ("smux_init: %s [%s]", ! 519: smux_error (smux_errno), smux_info); ! 520: else ! 521: rock_and_roll = 0; ! 522: .fi ! 523: .in -.5i ! 524: .sp ! 525: ! 526: .sh 3 "Opening" ! 527: .lp ! 528: Once \fIsmux_init\fR returns OK, ! 529: you should start selecting for writability on the file-descriptor returned. ! 530: Once select says your program can write to the fd, ! 531: your program should call the routine \fIsmux_simple_open\fR, ! 532: which establishes the SMUX association. ! 533: .lp ! 534: On failure, ! 535: \fIsmux_error\fR will be set to one of ! 536: \fIparameterMissing\fR, \fIinvalidOperation\fR, \fIinProgress\fR, ! 537: \fIsystemError\fR, \fIcongestion\fR, or \fIyouLoseBig\fR. ! 538: If the error code is \fIinProgress\fR, ! 539: then your program should continue retrying the fd for writability, ! 540: and then call \fIsmux_simple_open again. ! 541: Otherwise, ! 542: your program should take the appropriate action based on the error ! 543: code returned. ! 544: .sp ! 545: .in +.5i ! 546: .nf ! 547: if (smux_simple_open (&se -> se_identity, ! 548: "SMUX UNIX daemon", ! 549: se -> se_password, ! 550: strlen (se -> se_password)) ! 551: == NOTOK) { ! 552: if (smux_errno == inProgress) ! 553: return; ! 554: ! 555: error ("smux_simple_open: %s [%s]", ! 556: smux_error (smux_errno), smux_info); ! 557: smux_fd = NOTOK; ! 558: } ! 559: else ! 560: rock_and_roll = 1; ! 561: .fi ! 562: .in -.5i ! 563: .sp ! 564: ! 565: .sh 3 "Closing" ! 566: .lp ! 567: If, for some reason, your program wishes to close the SMUX ! 568: association. ! 569: There are several reasons that are allowed: ! 570: .sp ! 571: .in +.5i ! 572: .nf ! 573: goingDown ! 574: unsupportedVersion ! 575: packetFormat ! 576: protocolError ! 577: internalError ! 578: authenticationFailure ! 579: .fi ! 580: .in -.5i ! 581: .sp ! 582: On failure, ! 583: \fIsmux_error\fR will be set to one of ! 584: \fIinvalidOperation\fR, \fIcongestion\fR, or \fIyouLoseBig\fR. ! 585: ! 586: .sh 3 "Registering Subtrees" ! 587: .lp ! 588: Once \fIsmux_simple_open\fR returns OK, ! 589: your program should register the MIB module that your daemon will ! 590: export by calling \fIsmux_register\fR. ! 591: A subtree can be registered in one of three modes: ! 592: .sp ! 593: .in +.5i ! 594: .nf ! 595: readOnly ! 596: readWrite ! 597: delete ! 598: .fi ! 599: .in -.5i ! 600: .sp ! 601: which are all fairly obvious. ! 602: .lp ! 603: Note that a return value of OK from \fIsmux_register\fR means only ! 604: that the registration request was queued for the SNMP agent via the ! 605: SMUX protocol. ! 606: Some time later the SNMP agent's response will be forthcoming. ! 607: .lp ! 608: On failure, ! 609: \fIsmux_error\fR will be set to one of ! 610: \fIparameterMissing\fR, \fIinvalidOperation\fR, \fIcongestion\fR, or ! 611: \fIyouLoseBig\fR. ! 612: Your program should take the appropriate action based on the error ! 613: code returned. ! 614: .sp ! 615: .in +.5i ! 616: .nf ! 617: if (smux_register (subtree, -1, readOnly) == NOTOK) { ! 618: error ("smux_register: %s [%s]", ! 619: smux_error (smux_errno), smux_info); ! 620: smux_fd = NOTOK; ! 621: } ! 622: .fi ! 623: .in -.5i ! 624: .sp ! 625: ! 626: .sh 3 "Main Loop" ! 627: .lp ! 628: At this point, ! 629: your program is more or less in it's main loop ! 630: (actually, it probably entered the main loop after the very first call ! 631: to \fIsmux_init\fR). ! 632: .sp ! 633: .in +.5i ! 634: .nf ! 635: int nfds; /* these are set for other fd's... */ ! 636: ! 637: fd_set ifds; ! 638: fd_set ofds; ! 639: ! 640: for (;;) { ! 641: int n, ! 642: secs; ! 643: fd_set rfds, ! 644: wfds; ! 645: ! 646: secs = NOTOK; ! 647: ! 648: rfds = ifds; /* struct copy */ ! 649: wfds = ofds; /* .. */ ! 650: ! 651: if (smux_fd == NOTOK && !dont_bother_anymore) ! 652: secs = 5 * 60L; ! 653: else ! 654: if (rock_and_roll) ! 655: FD_SET (smux_fd, &rfds); ! 656: else ! 657: FD_SET (smux_fd, &wfds); ! 658: if (smux_fd >= nfds) ! 659: nfds = smux_fd + 1; ! 660: ! 661: if ((n = xselect (nfds, &rfds, &wfds, NULLFD, secs)) ! 662: == NOTOK) { ! 663: error ("xselect failed"); ! 664: \0 ... ! 665: } ! 666: ! 667: /* check fd's for other purposes here... */ ! 668: ! 669: if (smux_fd == NOTOK && !dont_bother_anymore) { ! 670: if (n == 0) { ! 671: if ((smux_fd = smux_init (debug)) == NOTOK) ! 672: error ("smux_init: %s [%s]", ! 673: smux_error (smux_errno), ! 674: smux_info); ! 675: else ! 676: rock_and_roll = 0; ! 677: } ! 678: } ! 679: else ! 680: if (rock_and_roll) { ! 681: if (FD_ISSET (smux_fd, &rfds)) ! 682: doit_smux (); ! 683: } ! 684: else ! 685: if (FD_ISSET (smux_fd, &wfds)) { ! 686: if (smux_simple_open (&se -> se_identity, ! 687: "SMUX UNIX daemon", ! 688: se -> se_password, ! 689: strlen (se -> se_password)) ! 690: == NOTOK) { ! 691: if (smux_errno != inProgress) { ! 692: error ("smux_simple_open: %s [%s]", ! 693: smux_error (smux_errno), ! 694: smux_info); ! 695: smux_fd = NOTOK; ! 696: } ! 697: } ! 698: else { ! 699: rock_and_roll = 1; ! 700: ! 701: if (smux_register (subtree, -1, ! 702: readOnly) == NOTOK) { ! 703: error ("smux_register: %s [%s]", ! 704: smux_error (smux_errno), ! 705: smux_info); ! 706: smux_fd = NOTOK; ! 707: } ! 708: } ! 709: } ! 710: .fi ! 711: .in -.5i ! 712: .sp ! 713: So, ! 714: all that remains is to take a look at the routine \fIdoit_smux\fR ! 715: mentioned above. ! 716: This is called when select indicates the SMUX file-descriptor is ready ! 717: for reading. ! 718: ! 719: .sh 3 "Events" ! 720: .lp ! 721: When \fIselect\fR indicates the SMUX file-descriptor is ready for ! 722: reading, ! 723: your program calls the routine \fIsmux_wait\fR to return the next ! 724: event from the SNMP agent. ! 725: .lp ! 726: Note that the event is filled-in from a static area. ! 727: On the next call to \fIsmux_init\fR, \fIsmux_close\fR, or ! 728: \fIsmux_wait\fR, ! 729: the value will be overwritten. ! 730: As such, ! 731: do not free this structure yourself. ! 732: .lp ! 733: On failure, ! 734: \fIsmux_error\fR will be set to one of ! 735: \fIparameterMissing\fR, \fIinvalidOperation\fR, \fIinProgress\fR, ! 736: or \fIyouLoseBig\fR. ! 737: Your program should take the appropriate action based on the error ! 738: code returned. ! 739: .sp ! 740: .in +.5i ! 741: .nf ! 742: struct type_SNMP_SMUX__PDUs *event; ! 743: ! 744: if (smux_wait (&event, NOTOK) == NOTOK) { ! 745: if (smux_errno == inProgress) ! 746: return; ! 747: ! 748: error ("smux_wait: %s [%s]", ! 749: smux_error (smux_errno), smux_info); ! 750: losing: ; ! 751: smux_fd = NOTOK; ! 752: return; ! 753: } ! 754: .fi ! 755: .in -.5i ! 756: .sp ! 757: Next, ! 758: your program should switch based on the actual event returned: ! 759: .sp ! 760: .in +.5i ! 761: .nf ! 762: type_SNMP_SMUX__PDUs_registerResponse ! 763: type_SNMP_SMUX__PDUs_get_request ! 764: type_SNMP_SMUX__PDUs_get__next__request ! 765: type_SNMP_SMUX__PDUs_close ! 766: .fi ! 767: .in -.5i ! 768: .sp ! 769: The actual code is fairly straight-forward: ! 770: .sp ! 771: .in +.5i ! 772: .nf ! 773: switch (event -> offset) { ! 774: case type_SNMP_SMUX__PDUs_registerResponse: ! 775: { ! 776: struct type_SNMP_RRspPDU *rsp = ! 777: event -> un.registerResponse; ! 778: ! 779: if (rsp -> parm == int_SNMP_RRspPDU_failure) { ! 780: error ("SMUX registration of %s failed", ! 781: oid2ode (subtree)); ! 782: dont_bother_anymore = 1; ! 783: (void) smux_close (goingDown); ! 784: break; ! 785: } ! 786: } ! 787: if (smux_trap (int_SNMP_generic__trap_coldStart, ! 788: 0, (struct type_SNMP_VarBindList *) 0) ! 789: == NOTOK) { ! 790: error ("smux_trap: %s [%s]", ! 791: smux_error (smux_errno), smux_info); ! 792: break; ! 793: } ! 794: return; ! 795: ! 796: case type_SNMP_SMUX__PDUs_get_request: ! 797: case type_SNMP_SMUX__PDUs_get__next__request: ! 798: get_smux (event -> un.get__request, event -> offset); ! 799: return; ! 800: ! 801: case type_SNMP_SMUX__PDUs_close: ! 802: notice ("SMUX close: %s", ! 803: smux_error (event -> un.close -> parm)); ! 804: break; ! 805: ! 806: default: ! 807: error ("bad SMUX operation: %d", event -> offset); ! 808: (void) smux_close (protocolError); ! 809: break; ! 810: } ! 811: smux_fd = NOTOK; ! 812: .fi ! 813: .in -.5i ! 814: .sp ! 815: Note the use of the \fIsmux_trap\fR routine to send a \fIcoldStart\fR ! 816: trap once the daemon has successful registered the MIB module it is exporting. ! 817: The trap codes are: ! 818: .sp ! 819: .in +.5i ! 820: .nf ! 821: int_SNMP_generic__trap_coldStart ! 822: int_SNMP_generic__trap_warmStart ! 823: int_SNMP_generic__trap_linkDown ! 824: int_SNMP_generic__trap_linkUp ! 825: int_SNMP_generic__trap_authenticationFailure ! 826: int_SNMP_generic__trap_egpNeighborLoss ! 827: int_SNMP_generic__trap_enterpriseSpecific ! 828: .fi ! 829: .in -.5i ! 830: .sp ! 831: If this routine fails, ! 832: \fIsmux_errno\fR will be set to one of ! 833: \fIinvalidOperation\fR, \fIcongestion\fR, or \fIyouLoseBig\fR. ! 834: .lp ! 835: So, ! 836: all that remains is to take a look at the routine \fIget_smux\fR which ! 837: implements the SNMP get and powerful get-next operators. ! 838: We will return to this routine once the structures and routines which ! 839: implement the managed object abstraction are explained. ! 840: ! 841: .bp ! 842: .sh 1 "Managed Objects" ! 843: .lp ! 844: A managed object is an abstraction with a syntax and a semantics. ! 845: The syntax defines what instances of the object look like on the network. ! 846: The semantics define what the object actually is. ! 847: ! 848: .sh 2 "Syntax" ! 849: .lp ! 850: The syntax of MIB objects is modeled by the \fIOS\fR structure: ! 851: .sp ! 852: .in +.5i ! 853: .nf ! 854: typedef struct object_syntax { ! 855: char *os_name; /* syntax name */ ! 856: ! 857: IFP os_encode; /* data -> PE */ ! 858: IFP os_decode; /* PE -> data */ ! 859: IFP os_free; /* free data */ ! 860: ! 861: IFP os_parse; /* str -> data */ ! 862: IFP os_print; /* data -> tty */ ! 863: ! 864: \0 ... ! 865: } object_syntax, *OS; ! 866: #define NULLOS ((OS) 0) ! 867: .fi ! 868: .in -.5i ! 869: .sp ! 870: The syntaxes defined by the Internet-standard MIB are already implemented: ! 871: .sp ! 872: .in +.5i ! 873: .nf ! 874: .ta \w'NetworkAddress 'u ! 875: syntax structure ! 876: INTEGER integer ! 877: OctetString struct qbuf (OCTET STRING) ! 878: ObjectID struct OIDentifier (OBJECT IDENTIFIER) ! 879: NULL char ! 880: DisplayString struct qbuf ! 881: IpAddress struct sockaddr_in ! 882: NetworkAddress struct sockaddr_in ! 883: Counter integer ! 884: Gauge integer ! 885: TimeTicks integer ! 886: ClnpAddress struct sockaddr_iso ! 887: .re ! 888: .fi ! 889: .in -.5i ! 890: .sp ! 891: where \*(lqsyntax\*(rq is the name appearing in a compiled MIB module, ! 892: and \*(lqstructure\*(rq is the \fIC\fR language data type ! 893: corresponding to the object's syntax. ! 894: .lp ! 895: To take a syntax name and get back the structure, use the routine ! 896: \fItext2syn\fR. ! 897: ! 898: .sh 3 "Abstractions for Standard Syntaxes" ! 899: .lp ! 900: Here are the structures and routine used to implement the low-level ! 901: MIB abstractions. ! 902: ! 903: .sh 4 "integer" ! 904: .lp ! 905: This is used for the \fIINTEGER\fR, \fICounter\fR, \fIGauge\fR, and ! 906: \fITimeTicks\fR syntax. ! 907: .lp ! 908: The definition is: ! 909: .sp ! 910: .in +.5i ! 911: .nf ! 912: typedef int integer; ! 913: .fi ! 914: .in -.5i ! 915: .sp ! 916: which is hardly surprising. ! 917: ! 918: .sh 4 "struct qbuf" ! 919: .lp ! 920: This is used for the \fIOctetString\fR (OBJECT IDENTIFIER) ! 921: and \fIDisplayString\fR syntaxes. ! 922: .lp ! 923: The definition is: ! 924: .sp ! 925: .in +.5i ! 926: .nf ! 927: struct qbuf { ! 928: struct qbuf *qb_forw; /* doubly-linked list */ ! 929: struct qbuf *qb_back; /* .. */ ! 930: ! 931: int qb_len; /* length of data */ ! 932: char *qb_data; /* current pointer into data */ ! 933: char qb_base[1]; /* extensible... */ ! 934: }; ! 935: .fi ! 936: .in -.5i ! 937: .sp ! 938: The macro \fIQBFREE\fR is used to traverse \fIqb_forw\fR to free all ! 939: qbufs in the ring: ! 940: .sp ! 941: .in +.5i ! 942: .nf ! 943: QBFREE (qb) ! 944: struct qbuf *qb; ! 945: .fi ! 946: .in -.5i ! 947: .sp ! 948: To allocate a new string from the qbuf use: ! 949: .sp ! 950: .in +.5i ! 951: .nf ! 952: char *qb2str (q) ! 953: struct qbuf *q ! 954: .fi ! 955: .in -.5i ! 956: .sp ! 957: The string is NULL-terminated, ! 958: but there may be other NULLs in the string to foil things like \fIstrlen\fR. ! 959: .lp ! 960: To allocate a new qbuf of \fIlen\fR octets from string \fIs\fR, ! 961: use: ! 962: .sp ! 963: .in +.5i ! 964: .nf ! 965: struct qbuf str2qb (s, len, head) ! 966: char *s; ! 967: int len, ! 968: head; ! 969: .fi ! 970: .in -.5i ! 971: .sp ! 972: (\fIhead\fR should always be \fI1\fR). ! 973: .lp ! 974: To free an allocated qbuf, use \fIqb_free\fR which calls \fIQBFREE\fR ! 975: and then \fIfree\fR on its argument. ! 976: ! 977: .sh 4 "struct OIDentifier" ! 978: .lp ! 979: This is used for the \fIObjectID\fR (OBJECT IDENTIFIER) syntax. ! 980: The definition is: ! 981: .sp ! 982: .in +.5i ! 983: .nf ! 984: typedef struct OIDentifier { ! 985: int oid_nelem; /* number of sub-identifiers */ ! 986: ! 987: unsigned int *oid_elements; ! 988: /* the (ordered) list of sub-identifiers */ ! 989: } OIDentifier, *OID; ! 990: #define NULLOID ((OID) 0) ! 991: .fi ! 992: .in -.5i ! 993: .sp ! 994: To compare two \fIOIDs\fR, use ! 995: .sp ! 996: .in +.5i ! 997: .nf ! 998: int oid_cmp (p, q) ! 999: OID p, ! 1000: q; ! 1001: .fi ! 1002: .in -.5i ! 1003: .sp ! 1004: which returns \fI-1\fR if \fIp<q\fR, \fI1\fR if \fIp>q\fR, \fI0\fR otherwise. ! 1005: .lp ! 1006: To allocate a new \fIOID\fR and copy it from another, use \fIoid_cpy\fR. ! 1007: .lp ! 1008: To free an allocated \fIOID\fR, use \fIoid_free\fR. ! 1009: .lp ! 1010: To take an \fIOID\fR and produce a string in numeric form use ! 1011: .sp ! 1012: .in +.5i ! 1013: .nf ! 1014: char *sprintoid (oid) ! 1015: OID oid; ! 1016: .fi ! 1017: .in -.5i ! 1018: .sp ! 1019: The result is returned in a static area. ! 1020: The inverse routine is: ! 1021: .sp ! 1022: .in +.5i ! 1023: .nf ! 1024: OID str2oid (s) ! 1025: char *s; ! 1026: .fi ! 1027: .in -.5i ! 1028: .sp ! 1029: which returns an OID from a static area ! 1030: ! 1031: .sh 4 "struct sockaddr_in" ! 1032: .lp ! 1033: This is used for the \fIIpAddress\fR and \fINetworkAddress\fR syntaxes. ! 1034: Presumably you are overly familiar with this structure. ! 1035: ! 1036: .sh 4 "struct sockaddr_iso" ! 1037: .lp ! 1038: This is used for the \fIClnpAddress\fR syntax. ! 1039: If your are not running a BSD/OSI system, ! 1040: don't worry about this. ! 1041: ! 1042: .sh 3 "Defining a new Syntax" ! 1043: .lp ! 1044: The routine \fIadd_syntax\fR is used to define a new syntax. ! 1045: (if this routine returns \fINOTOK\fR, ! 1046: then the internal syntax table has overflowed; ! 1047: adjust the constant \fIMAXSYN\fR in the file \fBsyntax.c\fR). ! 1048: The first argument is the name of the syntax. ! 1049: The other five are pointers to integer-valued routines that are called ! 1050: to operate on an opaque pointer: ! 1051: .sp ! 1052: .in +.5i ! 1053: .nf ! 1054: /* returns OK if x is encoded into pe (allocates pe), ! 1055: NOTOK otherwise */ ! 1056: ! 1057: f_encode (x, pe) ! 1058: pointer *x; ! 1059: PE *pe; ! 1060: ! 1061: ! 1062: /* returns OK if pe is decoded into x (allocates x), ! 1063: NOTOK otherwise */ ! 1064: ! 1065: f_decode (x, pe) ! 1066: pointer **x; ! 1067: PE pe; ! 1068: ! 1069: ! 1070: /* free's an allocated x (from f_decode or f_parse) */ ! 1071: ! 1072: f_free (x) ! 1073: pointer *x; ! 1074: ! 1075: ! 1076: /* returns OK if s is decoded into x (allocates x), ! 1077: NOTOK otherwise */ ! 1078: ! 1079: f_parse (x, s) ! 1080: pointer **x; ! 1081: char *s; ! 1082: ! 1083: ! 1084: /* prints x on the user's tty */ ! 1085: ! 1086: f_print (x, os) ! 1087: pointer *x; ! 1088: OS os; ! 1089: .fi ! 1090: .in -.5i ! 1091: .sp ! 1092: Presentation elements (PEs) are discussed later on. ! 1093: ! 1094: .sh 2 "Objects" ! 1095: .lp ! 1096: MIB objects are modeled by the \fIOT\fR structure: ! 1097: .sp ! 1098: .in +.5i ! 1099: .nf ! 1100: typedef struct object_type { ! 1101: char *ot_text; /* OBJECT DESCRIPTOR */ ! 1102: char *ot_id; /* OBJECT IDENTIFIER */ ! 1103: OID ot_name; /* .. */ ! 1104: ! 1105: OS ot_syntax; /* SYNTAX */ ! 1106: ! 1107: int ot_access; /* ACCESS */ ! 1108: #define OT_NONE 0x00 ! 1109: #define OT_RDONLY 0x01 ! 1110: #define OT_RDWRITE 0x02 ! 1111: ! 1112: int ot_status; /* STATUS */ ! 1113: #define OT_OBSOLETE 0x00 ! 1114: #define OT_MANDATORY 0x01 ! 1115: #define OT_OPTIONAL 0x02 ! 1116: #define OT_DEPRECATED 0x03 ! 1117: ! 1118: \0 ... ! 1119: } object_type, *OT; ! 1120: #define NULLOT ((OT) 0) ! 1121: .fi ! 1122: .in -.5i ! 1123: .sp ! 1124: There are lots of routines to manipulate MIB objects. ! 1125: .lp ! 1126: The routine \fIname2obj\fR takes an object identifier and returns the ! 1127: object type, either exact or prefix, e.g., ! 1128: .sp ! 1129: .in +.5i ! 1130: .nf ! 1131: name2obj ( OID { ipRouteDest.0.0.0.0 } ) ! 1132: .fi ! 1133: .in -.5i ! 1134: .sp ! 1135: returns the object type for \*(lqipRouteDest\*(rq ! 1136: .lp ! 1137: The routine \fItext2obj\fR takes a string and returns the exact object type, ! 1138: e.g., ! 1139: .sp ! 1140: .in +.5i ! 1141: .nf ! 1142: text2obj (\*(lqipRouteDest\*(rq) ! 1143: .fi ! 1144: .in -.5i ! 1145: .sp ! 1146: will succeed, but ! 1147: .sp ! 1148: .in +.5i ! 1149: .nf ! 1150: text2obj (\*(lqipRouteDest.0.0.0.0\*(rq) ! 1151: .fi ! 1152: .in -.5i ! 1153: .sp ! 1154: will fail. ! 1155: .lp ! 1156: The routine \fItext2oid\fR takes a string and returns the object identifier ! 1157: associated with the corresponding object. ! 1158: The string can be numeric (e.g., \*(lq1.3.6.1\*(rq), ! 1159: symbolic (e.g., \*(lqinternet\*(rq), ! 1160: or symbolic.numeric (e.g., \*(lqiso.3.6.1\*(rq). ! 1161: .lp ! 1162: The routine \fIoid2ode\fR takes an \fIOID\fR and returns a string suitable ! 1163: for pretty-printing, in the form symbolic or symbolic.numeric. ! 1164: ! 1165: .sh 2 "Instances" ! 1166: .lp ! 1167: MIB instances are modeled by the \fIOI\fR structure: ! 1168: .sp ! 1169: .in +.5i ! 1170: .nf ! 1171: typedef struct object_instance { ! 1172: OID oi_name; /* instance OID */ ! 1173: ! 1174: OT oi_type; /* prototype */ ! 1175: } object_instance, *OI; ! 1176: #define NULLOI ((OI) 0) ! 1177: .fi ! 1178: .in -.5i ! 1179: .sp ! 1180: There are lots of routines to manipulate MIB instances. ! 1181: .lp ! 1182: The routine \fIname2inst\fR takes a variable name and returns the ! 1183: corresponding instance, e.g., ! 1184: .sp ! 1185: .in +.5i ! 1186: .nf ! 1187: name2inst ( OID { ipRouteDest.0.0.0.0 } ) ! 1188: .fi ! 1189: .in -.5i ! 1190: .sp ! 1191: will return an \fIOI\fR with \fIoi_name\fR set to its argument and ! 1192: \fIoi_type\fR set to the object type for \*(lqipRouteDest\*(rq. ! 1193: .lp ! 1194: The routine \fInext2inst\fR finds the closest object type before the ! 1195: variable name and returns an \fIOI\fR corresponding to that object type. ! 1196: .lp ! 1197: The routine \fItext2inst\fR first calls \fItext2oid\fR to get the \fIOID\fR ! 1198: corresponding to the argument, then calls \fIname2obj\fR to get the ! 1199: type associated with that \fIOID\fR. ! 1200: ! 1201: .bp ! 1202: .sh 1 "Linkage" ! 1203: .lp ! 1204: It is now time to tie up all the loose ends. ! 1205: When your program starts, ! 1206: it needs to establish some linkage between the objects defined in the ! 1207: compiled MIB module and the data structures in your program. ! 1208: Further, ! 1209: when the SNMP agent asks to manipulate the object instances in the MIB ! 1210: module your program exported (e.g., using the powerful SNMP get-next ! 1211: operator), you need to have a small protocol engine to field these requests. ! 1212: .lp ! 1213: The way the linkage is established is to associate a \fIC\fR routine ! 1214: with each of the leaf objects defined in the compiled MIB module. ! 1215: When the protocol engine fields a request from the SNMP agent, ! 1216: it will invoke that routine to \*(lqdo the right thing\*(rq. ! 1217: Typically, ! 1218: for each table in the compiled MIB module, ! 1219: you define a \fIC\fR routine to handle requests for the leaf objects ! 1220: of that table. ! 1221: In addition, ! 1222: there is usually one more routine defined to handle those leaf objects ! 1223: not found under any one table. ! 1224: For each \fIC\fR routine you define, ! 1225: you define a group of constant integer symbols which identify a leaf ! 1226: object that is handled by that routine ! 1227: (e.g., for each routine, you start numbering the symbols starting at ! 1228: \fI0\fR and going up). ! 1229: By convention, ! 1230: these symbols have the same name as the leaf objects. ! 1231: ! 1232: .sh 2 "Initialization" ! 1233: .lp ! 1234: Earlier it was noted that your program will probably call a routine ! 1235: called \fIinit_mib\fR which initializes it's internal MIB structures. ! 1236: The routine should look something like this: ! 1237: .sp ! 1238: .in +.5i ! 1239: .nf ! 1240: register OT ot; ! 1241: ! 1242: if (ot = text2obj ("mbufS")) ! 1243: ot -> ot_getfnx = o_mbuf, ! 1244: ot -> ot_info = (caddr_t) mbufS; ! 1245: ! 1246: \0... ! 1247: ! 1248: if (ot = text2obj ("mbufType")) ! 1249: ot -> ot_getfnx = o_mbufType, ! 1250: ot -> ot_info = (caddr_t) mbufType; ! 1251: .fi ! 1252: .in -.5i ! 1253: .sp ! 1254: where we can imagine that \fIo_mbuf\fR and \fIo_mbufType\fR are ! 1255: routines that are defined in your program, ! 1256: and \fImbufS\fR and \fImbufType\fR are constant symbols defined with ! 1257: those routines. ! 1258: ! 1259: .sh 2 "Generic Event Handling" ! 1260: .lp ! 1261: Earlier it was noted that your program will probably call a routine ! 1262: called \fIget_smux\fR which implements the SNMP get and powerful ! 1263: get-next operators. ! 1264: The routine should look something like this: ! 1265: .sp ! 1266: .in +.5i ! 1267: .nf ! 1268: static get_smux (pdu, offset) ! 1269: register struct type_SNMP_GetRequest__PDU *pdu; ! 1270: int offset; ! 1271: { ! 1272: int idx, ! 1273: status; ! 1274: object_instance ois; ! 1275: register struct type_SNMP_VarBindList *vp; ! 1276: ! 1277: quantum = pdu -> request__id; ! 1278: idx = 0; ! 1279: for (vp = pdu -> variable__bindings; vp; vp = vp -> next) { ! 1280: register OI oi; ! 1281: register OT ot; ! 1282: register struct type_SNMP_VarBind *v = vp -> VarBind; ! 1283: ! 1284: idx++; ! 1285: ! 1286: if (offset == type_SNMP_SMUX__PDUs_get__next__request) { ! 1287: if ((oi = name2inst (v -> name)) == NULLOI ! 1288: && (oi = next2inst (v -> name)) == NULLOI) ! 1289: goto no_name; ! 1290: ! 1291: if ((ot = oi -> oi_type) -> ot_getfnx == NULLIFP) ! 1292: goto get_next; ! 1293: } ! 1294: else ! 1295: if ((oi = name2inst (v -> name)) == NULLOI ! 1296: || (ot = oi -> oi_type) -> ot_getfnx ! 1297: == NULLIFP) { ! 1298: no_name: ; ! 1299: pdu -> error__status = ! 1300: int_SNMP_error__status_noSuchName; ! 1301: goto out; ! 1302: } ! 1303: ! 1304: try_again: ; ! 1305: switch (ot -> ot_access) { ! 1306: case OT_NONE: ! 1307: if (offset == ! 1308: type_SNMP_SMUX__PDUs_get__next__request) ! 1309: goto get_next; ! 1310: goto no_name; ! 1311: ! 1312: case OT_RDONLY: ! 1313: if (offset == ! 1314: type_SNMP_SMUX__PDUs_set__request) { ! 1315: pdu -> error__status = ! 1316: int_SNMP_error__status_readOnly; ! 1317: goto out; ! 1318: } ! 1319: break; ! 1320: ! 1321: case OT_RDWRITE: ! 1322: break; ! 1323: } ! 1324: ! 1325: switch (status = (*ot -> ot_getfnx) (oi, v, offset)) { ! 1326: case NOTOK: /* get-next wants a bump */ ! 1327: get_next: ; ! 1328: oi = &ois; ! 1329: for (;;) { ! 1330: if ((ot = ot -> ot_next) == NULLOT) { ! 1331: pdu -> error__status = ! 1332: int_SNMP_error__status_noSuchName; ! 1333: goto out; ! 1334: } ! 1335: oi -> oi_name = ! 1336: (oi -> oi_type = ot) -> ot_name; ! 1337: if (ot -> ot_getfnx) ! 1338: goto try_again; ! 1339: } ! 1340: ! 1341: case int_SNMP_error__status_noError: ! 1342: break; ! 1343: ! 1344: default: ! 1345: pdu -> error__status = status; ! 1346: goto out; ! 1347: } ! 1348: } ! 1349: idx = 0; ! 1350: ! 1351: out: ; ! 1352: pdu -> error__index = idx; ! 1353: ! 1354: if (smux_response (pdu) == NOTOK) { ! 1355: error ("smux_response: %s [%s]", ! 1356: smux_error (smux_errno), smux_info); ! 1357: smux_fd = NOTOK; ! 1358: } ! 1359: } ! 1360: .fi ! 1361: .in -.5i ! 1362: .sp ! 1363: The actual code is fairly straight-forward: ! 1364: First, ! 1365: the variable \fIquantum\fR is set to the request ID for this transaction. ! 1366: The SMUX protocol requires that this number change for each SNMP ! 1367: operation that the SNMP agent fields, ! 1368: so your program can use it as a cookie to see when it should re-read ! 1369: kernel variables. ! 1370: (A single SNMP operation received by the SNMP agent might result in ! 1371: multiple SMUX protocol transactions with your program.) ! 1372: .lp ! 1373: Next, the code loops through the list of variables requested. ! 1374: The object instance is determined and loaded into \fIoi\fRn ! 1375: and the corresponding object type is loaded into \fIot\fR, ! 1376: and the access is checked. ! 1377: .lp ! 1378: Finally, the user-defined routine is invoked. ! 1379: This routine returns one of these values: ! 1380: .sp ! 1381: .in +.5i ! 1382: .nf ! 1383: NOTOK ! 1384: int_SNMP_error__status_noError ! 1385: int_SNMP_error__status_tooBig ! 1386: int_SNMP_error__status_noSuchName ! 1387: int_SNMP_error__status_badValue ! 1388: int_SNMP_error__status_readOnly ! 1389: int_SNMP_error__status_genErr ! 1390: .fi ! 1391: .in -.5i ! 1392: .sp ! 1393: the first value is returned only if the powerful get-next operator is ! 1394: being invoked and the routine didn't have any more object instances ! 1395: for the object type in question. ! 1396: The remainder are all self-explanatory. ! 1397: .lp ! 1398: Once an answer is returned, ! 1399: the loop either continues or is broken and a response is written back ! 1400: using the \fIsmux_response\fR routine. ! 1401: On failure, ! 1402: \fIsmux_error\fR will be set to one of ! 1403: \fIparameterMissing\fR, \fIinvalidOperation\fR, or \fIyouLoseBig\fR. ! 1404: ! 1405: .sh 2 "Specific Event Handling: Outside a table" ! 1406: .lp ! 1407: Now let's look at one of these user-defined routines, ! 1408: which handles leaf objects that are not part of a table: ! 1409: .sp ! 1410: .in +.5i ! 1411: .nf ! 1412: static int lastq = -1; ! 1413: static struct mbstat mbstat; ! 1414: ! 1415: ! 1416: #define mbufS 0 ! 1417: #define mbufClusters 1 ! 1418: \0... ! 1419: #define mbufFrees 6 ! 1420: ! 1421: ! 1422: static int o_mbuf (oi, v, offset) ! 1423: OI oi; ! 1424: register struct type_SNMP_VarBind *v; ! 1425: int offset; ! 1426: { ! 1427: int ifvar; ! 1428: register struct mbstat *m = &mbstat; ! 1429: register OID oid = oi -> oi_name; ! 1430: register OT ot = oi -> oi_type; ! 1431: ! 1432: ifvar = (int) ot -> ot_info; ! 1433: switch (offset) { ! 1434: case type_SNMP_SMUX__PDUs_get__request: ! 1435: if (oid -> oid_nelem != ! 1436: ot -> ot_name -> oid_nelem + 1 ! 1437: || oid -> oid_elements[oid -> oid_nelem - 1] ! 1438: != 0) ! 1439: return int_SNMP_error__status_noSuchName; ! 1440: break; ! 1441: ! 1442: case type_SNMP_SMUX__PDUs_get__next__request: ! 1443: if (oid -> oid_nelem ! 1444: == ot -> ot_name -> oid_nelem) { ! 1445: OID new; ! 1446: ! 1447: if ((new = oid_extend (oid, 1)) == NULLOID) ! 1448: return int_SNMP_error__status_genErr; ! 1449: new -> oid_elements[new -> oid_nelem - 1] = 0; ! 1450: ! 1451: if (v -> name) ! 1452: free_SNMP_ObjectName (v -> name); ! 1453: v -> name = new; ! 1454: } ! 1455: else ! 1456: return NOTOK; ! 1457: break; ! 1458: ! 1459: default: ! 1460: return int_SNMP_error__status_genErr; ! 1461: } ! 1462: ! 1463: if (quantum != lastq) { ! 1464: lastq = quantum; ! 1465: ! 1466: if (getkmem (nl + N_MBSTAT, (caddr_t) m, sizeof *m) ! 1467: == NOTOK) ! 1468: return int_SNMP_error__status_genErr; ! 1469: } ! 1470: ! 1471: switch (ifvar) { ! 1472: case mbufS: ! 1473: return o_integer (oi, v, m -> m_mbufs); ! 1474: ! 1475: case mbufClusters: ! 1476: return o_integer (oi, v, m -> m_clusters); ! 1477: ! 1478: \0... ! 1479: ! 1480: case mbufFrees: ! 1481: return o_integer (oi, v, m -> m_mbfree); ! 1482: ! 1483: default: ! 1484: return int_SNMP_error__status_noSuchName; ! 1485: } ! 1486: } ! 1487: .fi ! 1488: .in -.5i ! 1489: .sp ! 1490: The actual code is fairly straight-forward: ! 1491: First, ! 1492: the constant offsets are defined. ! 1493: Then, the \fIo_mbuf\fR routine is defined. ! 1494: .lp ! 1495: The first action is to determine which leaf object is being referenced. ! 1496: This corresponding symbolic constant is placed in the variable \fIifvar\fR. ! 1497: Then a switch is made based on the operation. ! 1498: .np ! 1499: For the get operation, ! 1500: all instances are identified by the object type followed by \*(lq.0\*(rq ! 1501: (e.g., \*(lqmbufS.0\*(rq). ! 1502: So, ! 1503: the code checks to see if the \fIOID\fR associated with the object ! 1504: instance is exactly one longer than the \fIOID\fR associated with the ! 1505: object type, ! 1506: and that the extra sub-identifier has the value \fI0\fR. ! 1507: .np ! 1508: For the powerful get-next operation, ! 1509: there are really two cases, ! 1510: depending on whether some instance identifier is present. ! 1511: If some instance identifier is present, ! 1512: then for a non-tabular leaf object, ! 1513: the next variable belongs to some other object type, ! 1514: so the routine simply returns the value \fINOTOK\fR, ! 1515: and the \fIget_smux\fR routine will find the next object accordingly. ! 1516: Otherwise, ! 1517: if no instance is identified, ! 1518: a new \fIOID\fR is constructed and initialized. ! 1519: The old \fIOID\fR is free'd and the new one inserted in its place. ! 1520: .lp ! 1521: Now that the correct instance has been identified, ! 1522: a check is made to see if \fIkmem\fR should be consulted. ! 1523: (Obviously other programs might consult other data stores.) ! 1524: Finally, ! 1525: the instance value is encoded and the routine returns. ! 1526: ! 1527: .sh 3 "Instance handling" ! 1528: .lp ! 1529: Several routines are provided to encode instance values: ! 1530: .lp ! 1531: The \fIo_number\fR routine encodes an integer value, ! 1532: such as an INTEGER, Counter, Guage, or TimeTick. ! 1533: The macro \fIo_integer\fR is simply a synonym for \fIo_number\fR. ! 1534: .lp ! 1535: The \fIo_string\fR routine encodes a string value, ! 1536: such as an OctetString or DisplayString, ! 1537: e.g., ! 1538: .sp ! 1539: .in +.5i ! 1540: .nf ! 1541: o_string (oi, v, "lo0", strlen ("lo0")); ! 1542: .fi ! 1543: .in -.5i ! 1544: .sp ! 1545: or ! 1546: .sp ! 1547: .in +.5i ! 1548: .nf ! 1549: o_string (oi, v, ether_addr, sizeof ether_addr); ! 1550: .fi ! 1551: .in -.5i ! 1552: .sp ! 1553: .lp ! 1554: The \fIo_specific\fR routine takes a structure corresponding to a ! 1555: syntax, ! 1556: such as an \fIOID\fR for ObjectID, ! 1557: and does the encoding, ! 1558: e.g., ! 1559: .sp ! 1560: .in +.5i ! 1561: .nf ! 1562: OID nullSpecific = ...; ! 1563: ! 1564: o_specific (oi, v, nullSpecific); ! 1565: .fi ! 1566: .in +.5i ! 1567: .sp ! 1568: ! 1569: .sh 3 "A special case" ! 1570: .lp ! 1571: A special user-defined routine is provided for those cases for ! 1572: leaf-objects containing information that is initialized on start-up, ! 1573: \fIo_generic\fR. ! 1574: For example: ! 1575: .sp ! 1576: .in +.5i ! 1577: .nf ! 1578: char buffer[BUFSIZ]; ! 1579: ! 1580: if (ot = text2obj ("sysName")) ! 1581: ot -> ot_getfnx = o_generic, ! 1582: ot -> ot_info = (caddr_t) sysName; ! 1583: ! 1584: (void) gethostname (buffer, sizeof buffer); ! 1585: (void) (*ot -> ot_syntax -> os_parse) ! 1586: ((struct qbuf **) &ot -> ot_info, buffer); ! 1587: .fi ! 1588: .in -.5i ! 1589: .sp ! 1590: The idea is that the \fIot_info\fR field contains a pointer to a ! 1591: data structure corresponding to the syntax of the object. ! 1592: Since all of the structures are rather strange (except for integers), ! 1593: \fIo_generic\fR probably won't receive a lot of use. ! 1594: ! 1595: .sh 2 "Specific Event Handling: Inside a table" ! 1596: .lp ! 1597: Now let's look at one of these user-defined routines, ! 1598: which handles leaf objects that are part of a table: ! 1599: .sp ! 1600: .in +.5i ! 1601: .nf ! 1602: #define mbufType 0 ! 1603: #define mbufAllocates 1 ! 1604: ! 1605: ! 1606: static int o_mbufType (oi, v, offset) ! 1607: OI oi; ! 1608: register struct type_SNMP_VarBind *v; ! 1609: int offset; ! 1610: { ! 1611: int ifnum, ! 1612: ifvar; ! 1613: register struct mbstat *m = &mbstat; ! 1614: register OID oid = oi -> oi_name; ! 1615: register OT ot = oi -> oi_type; ! 1616: ! 1617: ifvar = (int) ot -> ot_info; ! 1618: switch (offset) { ! 1619: case type_SNMP_SMUX__PDUs_get__request: ! 1620: if (oid -> oid_nelem ! 1621: != ot -> ot_name -> oid_nelem + 1) ! 1622: return int_SNMP_error__status_noSuchName; ! 1623: ifnum = ! 1624: oid -> oid_elements[oid -> oid_nelem - 1]; ! 1625: if (ifvar >= sizeof m -> m_mtypes / ! 1626: sizeof m -> m_mtypes[0]) ! 1627: return int_SNMP_error__status_noSuchName; ! 1628: break; ! 1629: ! 1630: case type_SNMP_SMUX__PDUs_get__next__request: ! 1631: if (oid -> oid_nelem ! 1632: == ot -> ot_name -> oid_nelem) { ! 1633: OID new; ! 1634: ! 1635: ifnum = 0; ! 1636: ! 1637: if ((new = oid_extend (oid, 1)) == NULLOID) ! 1638: return int_SNMP_error__status_genErr; ! 1639: new -> oid_elements[new -> oid_nelem - 1] = ! 1640: ifnum; ! 1641: ! 1642: if (v -> name) ! 1643: free_SNMP_ObjectName (v -> name); ! 1644: v -> name = new; ! 1645: } ! 1646: else { ! 1647: int i = ot -> ot_name -> oid_nelem; ! 1648: ! 1649: ifnum = oid -> oid_elements[i] + 1; ! 1650: if (ifnum >= sizeof m -> m_mtypes / ! 1651: sizeof m -> m_mtypes[0]) ! 1652: return NOTOK; ! 1653: ! 1654: oid -> oid_elements[i] = ifnum; ! 1655: oid -> oid_nelem = i + 1; ! 1656: } ! 1657: break; ! 1658: ! 1659: default: ! 1660: return int_SNMP_error__status_genErr; ! 1661: } ! 1662: ! 1663: if (quantum != lastq) { ! 1664: lastq = quantum; ! 1665: ! 1666: if (getkmem (nl + N_MBSTAT, (caddr_t) m, sizeof *m) ! 1667: == NOTOK) ! 1668: return int_SNMP_error__status_genErr; ! 1669: } ! 1670: ! 1671: switch (ifvar) { ! 1672: case mbufType: ! 1673: return o_integer (oi, v, ifnum); ! 1674: ! 1675: case mbufAllocates: ! 1676: return o_integer (oi, v, ! 1677: m -> m_mtypes[ifnum]); ! 1678: ! 1679: default: ! 1680: return int_SNMP_error__status_noSuchName; ! 1681: } ! 1682: } ! 1683: .fi ! 1684: .in -.5i ! 1685: .sp ! 1686: The actual code is fairly straight-forward: ! 1687: First, ! 1688: the constant offsets are defined. ! 1689: Then, the \fIo_mbufType\fR routine is defined. ! 1690: .lp ! 1691: The first action is to determine which leaf object is being referenced. ! 1692: This corresponding symbolic constant is placed in the variable \fIifvar\fR. ! 1693: Then a switch is made based on the operation. ! 1694: Because these are tabular objects, ! 1695: the instance refers to a row in the table ! 1696: (and the leaf object refers to a column in the table, of course). ! 1697: .np ! 1698: For the get operation, ! 1699: all instances are identified by the object type followed by ! 1700: \*(lq.\fIrowno\fR\*(rq ! 1701: (e.g., \*(lqmbufAllocates.10\*(rq) refers to the value in the ! 1702: \fImbufAllocates\fR column of the thenth row). ! 1703: So, ! 1704: the code checks to see if the \fIOID\fR associated with the object ! 1705: instance is exactly one longer than the \fIOID\fR associated with the ! 1706: object type, ! 1707: and that the extra sub-identifier is within the range 0..rowno-1. ! 1708: .np ! 1709: For the powerful get-next operation, ! 1710: there are really two cases, ! 1711: depending on whether some instance identifier is present. ! 1712: If no instance is identified, ! 1713: a new \fIOID\fR is constructed and initialized for the first row ! 1714: (row 0) of the table. ! 1715: The old \fIOID\fR is free'd and the new one inserted in its place. ! 1716: Otherwise, ! 1717: if some instance identifier is present, ! 1718: then the corresponding row must be identified, ! 1719: the row number incremented, and a check made to see if this is still ! 1720: in range ! 1721: (if not, \fINOTOK\fR is returned to signal that the next object type ! 1722: should be used). ! 1723: Otherwise, ! 1724: the \fIOID\fR is updated in place. ! 1725: .lp ! 1726: Now that the correct instance has been identified, ! 1727: a check is made to see if \fIkmem\fR should be consulted. ! 1728: (Obviously other programs might consult other data stores.) ! 1729: Finally, ! 1730: the instance value is encoded and the routine returns. ! 1731: ! 1732: .sh 3 "The general case" ! 1733: .lp ! 1734: Obviously, ! 1735: this is a simple example of table handling. ! 1736: In general, the table will be implemented via linked lists, ! 1737: sorted according to object instance. ! 1738: In this case, ! 1739: there is usually a special routine that is called to get a particular ! 1740: instance or the next instance. ! 1741: Take a look at the routines \fIget_tbent\fR and \fIo_smuxTree\fR in ! 1742: the file \fIsnmpd.c\fR. ! 1743: These implement the \fIsmuxTree\fR table. ! 1744: ! 1745: .\" some day talk about: ! 1746: .\" PEs ! 1747: .\" mediaddr2oid ! 1748: ! 1749: .bp ! 1750: .lp ! 1751: \fBTable of Contents\fR ! 1752: .sp 2 ! 1753: .xp t
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.