|
|
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.