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