1 /*	$NetBSD: ntpSnmpSubagentObject.c,v 1.1.1.1 2009/12/13 16:56:34 kardel Exp $	*/
2 
3 /*****************************************************************************
4  *
5  *  ntpSnmpSubAgentObject.c
6  *
7  *  This file provides the callback functions for net-snmp and registers the
8  *  serviced MIB objects with the master agent.
9  *
10  *  Each object has its own callback function that is called by the
11  *  master agent process whenever someone queries the corresponding MIB
12  *  object.
13  *
14  *  At the moment this triggers a full send/receive procedure for each
15  *  queried MIB object, one of the things that are still on my todo list:
16  *  a caching mechanism that reduces the number of requests sent to the
17  *  ntpd process.
18  *
19  ****************************************************************************/
20 #include <net-snmp/net-snmp-config.h>
21 #include <net-snmp/net-snmp-includes.h>
22 #include <net-snmp/agent/net-snmp-agent-includes.h>
23 #include "ntpSnmpSubagentObject.h"
24 #include <libntpq.h>
25 
26 /* general purpose buffer length definition */
27 #define NTPQ_BUFLEN 2048
28 
29 static int      ntpSnmpSubagentObject = 3;
30 
31 char ntpvalue[NTPQ_BUFLEN];
32 
33 
34 
35 /*****************************************************************************
36  *
37  * ntpsnmpd_strip_string
38  *
39  *  This function removes white space characters and EOL chars
40  *  from the beginning and end of a given NULL terminated string.
41  *  Be aware that the parameter itself is altered.
42  *
43  ****************************************************************************
44  * Parameters:
45  *	string		char*	The name of the string variable
46  *						NOTE: must be NULL terminated!
47  * Returns:
48  *	int		length of resulting string (i.e. w/o white spaces)
49  ****************************************************************************/
50 
51 int ntpsnmpd_strip_string(char *string)
52 {
53 	char newstring[2048] = { 0 };
54 	int i = 0;
55 	int j = 0;
56 
57 	if ( strlen(string) > 2047 )
58 		string[2048]=0;
59 
60 	j = strlen(string);
61 
62 	for (i=0;i<strlen(string);i++)
63 	{
64 		switch(string[i])
65 		{
66 			case 0x09: 	// Tab
67 			case 0x0A:	// LF
68 			case 0x0D:	// CR
69 			case ' ':  	// Space
70 			  break;
71 			default:
72 			  strncpy(newstring,(char *) &string[i], sizeof(newstring));
73 			  i=2048;
74 			  break;
75 		}
76 	}
77 	strncpy(string, newstring, j);
78 
79 	return(strlen(string));
80 }
81 
82 
83 /*****************************************************************************
84  *
85  * ntpsnmpd_parse_string
86  *
87  *  This function will parse a given NULL terminated string and cut it
88  *  into a fieldname and a value part (using the '=' as the delimiter.
89  *  The fieldname will be converted to uppercase and all whitespace
90  *  characters are removed from it.
91  *  The value part is stripped, e.g. all whitespace characters are removed
92  *  from the beginning and end of the string.
93  *  If the value is started and ended with quotes ("), they will be removed
94  *  and everything between the quotes is left untouched (including
95  *  whitespace)
96  *  Example:
97  *     server host name =   hello world!
98  *  will result in a field string "SERVERHOSTNAME" and a value
99  *  of "hello world!".
100  *     My first Parameter		=		"  is this!    "
101   * results in a field string "MYFIRSTPARAMETER" and a vaue " is this!    "
102  ****************************************************************************
103  * Parameters:
104  *	src			char*	The name of the source string variable
105  *						NOTE: must be NULL terminated!
106  *	field			char*	The name of the string which takes the
107  *						fieldname
108  *	fieldsize		int		The maximum size of the field name
109  *	value		char*	The name of the string which takes the
110  *						value part
111  *	valuesize		int		The maximum size of the value string
112  *
113  * Returns:
114  *	int			length of value string
115  ****************************************************************************/
116 
117 int ntpsnmpd_parse_string(char *src, char *field, int fieldsize, char *value, int valuesize)
118 {
119 	char string[2048];
120 	int i = 0;
121 	int j = 0;
122 	int l = 0;
123 	int a = 0;
124 
125 	strncpy(string,  src, sizeof(string));
126 
127 	a = strlen(string);
128 
129 	/* Parsing the field name */
130 	for (i=0;l==0;i++)
131 	{
132 		if (i>=a)
133 		   l=1;
134 		else
135 		{
136 			switch(string[i])
137 			{
138 				case 0x09: 	// Tab
139 				case 0x0A:	// LF
140 				case 0x0D:	// CR
141 				case ' ':  	// Space
142 				  break;
143 				case '=':
144 				  l=1;
145 				  break;
146 
147 				default:
148 				  if ( j < fieldsize )
149 				  {
150 					if ( ( string[i] >= 'a' ) && ( string[i] <='z' ) )
151 						field[j++]=( string[i] - 32 ); // convert to Uppercase
152 					else
153 						field[j++]=string[i];
154 				  }
155 
156 			}
157 		}
158 	}
159 
160 	field[j]=0; j=0; value[0]=0;
161 
162 
163 	/* Now parsing the value */
164 	for (l=0;i<a;i++)
165 	{
166 		if ( ( string[i] > 0x0D ) && ( string[i] != ' ' ) )
167 		   l = j+1;
168 
169 		if ( ( value[0] != 0 ) || ( ( string[i] > 0x0D ) && ( string[i] != ' ' ) ) )
170 		{
171 			if (j < valuesize )
172 			   value[j++]=string[i];
173 		}
174 	}
175 
176 	value[l]=0;
177 
178 	if ( value[0]=='"' )
179 		strcpy(value, (char *) &value[1]);
180 
181 	if ( value[strlen(value)-1] == '"' )
182 		value[strlen(value)-1]=0;
183 
184 	return (strlen(value));
185 
186 }
187 
188 
189 /*****************************************************************************
190  *
191  * ntpsnmpd_cut_string
192  *
193  *  This function will parse a given NULL terminated string and cut it
194  *  into fields using the specified delimiter character.
195  *  It will then copy the requested field into a destination buffer
196  *  Example:
197  *     ntpsnmpd_cut_string(read:my:lips:fool, RESULT, ':', 2, sizeof(RESULT))
198  *  will copy "lips" to RESULT.
199  ****************************************************************************
200  * Parameters:
201  *	src			char*	The name of the source string variable
202  *						NOTE: must be NULL terminated!
203  *	dest			char*	The name of the string which takes the
204  *						requested field content
205  * 	delim			char	The delimiter character
206  *	fieldnumber		int		The number of the required field
207  *						(start counting with 0)
208  *	maxsize			int		The maximum size of dest
209  *
210  * Returns:
211  *	int			length of resulting dest string
212  ****************************************************************************/
213 
214 int ntpsnmpd_cut_string(char *src, char *dest, const char delim, int fieldnumber, int maxsize)
215 {
216 	char string[2048];
217 	int i = 0;
218 	int j = 0;
219 	int l = 0;
220 	int a = 0;
221 
222 	strncpy (string, src, sizeof(string));
223 
224 	a = strlen(string);
225 
226         memset (dest, 0, maxsize);
227 
228 	/* Parsing the field name */
229 	for (i=0;l<=fieldnumber;i++)
230 	{
231 		if (i>=a)
232 		   l=fieldnumber+1; /* terminate loop */
233 		else
234 		{
235 			if ( string[i] == delim )
236 			{
237 				  l++; /* next field */
238 			}
239 			else  if ( ( l == fieldnumber) && ( j < maxsize )  )
240 			{
241 				dest[j++]=string[i];
242 			}
243 
244 		}
245 	}
246 
247 	return (strlen(dest));
248 
249 }
250 
251 
252 /*****************************************************************************
253  *
254  *  read_ntp_value
255  *
256  *  This function retrieves the value for a given variable, currently
257  *  this only supports sysvars. It starts a full mode 6 send/receive/parse
258  *  iteration and needs to be optimized, e.g. by using a caching mechanism
259  *
260  ****************************************************************************
261  * Parameters:
262  *	variable	char*	The name of the required variable
263  *	rbuffer		char*	The buffer where the value goes
264  *	maxlength	int	Max. number of bytes for resultbuf
265  *
266  * Returns:
267  *	u_int		number of chars that have been copied to
268  *			rbuffer
269  ****************************************************************************/
270 
271 unsigned int read_ntp_value(char *variable, char *rbuffer, unsigned int maxlength)
272 {
273 	unsigned int i, sv_len = 0;
274 	char sv_data[NTPQ_BUFLEN];
275 
276 	memset (sv_data,0, NTPQ_BUFLEN);
277 	sv_len= ntpq_read_sysvars ( sv_data, NTPQ_BUFLEN );
278 
279 	if ( sv_len )
280 	{
281 		i=ntpq_getvar( sv_data, sv_len , variable, rbuffer, maxlength);
282 		return i;
283 	} else {
284 		return 0;
285 	}
286 
287 }
288 
289 
290 /*****************************************************************************
291  *
292  *  The get_xxx functions
293  *
294  *  The following function calls are callback functions that will be
295  *  used by the master agent process to retrieve a value for a requested
296  *  MIB object.
297  *
298  ****************************************************************************/
299 
300 
301 int get_ntpEntSoftwareName (netsnmp_mib_handler *handler,
302                                netsnmp_handler_registration *reginfo,
303                                netsnmp_agent_request_info *reqinfo,
304                                netsnmp_request_info *requests)
305 {
306  char ntp_softwarename[NTPQ_BUFLEN];
307 
308    memset (ntp_softwarename, 0, NTPQ_BUFLEN);
309 
310    switch (reqinfo->mode) {
311    case MODE_GET:
312    {
313 	if ( read_ntp_value("product", ntpvalue, NTPQ_BUFLEN) )
314        {
315 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
316                              (u_char *)ntpvalue,
317                              strlen(ntpvalue)
318                             );
319        }
320     else  if ( read_ntp_value("version", ntpvalue, NTPQ_BUFLEN) )
321     {
322 	ntpsnmpd_cut_string(ntpvalue, ntp_softwarename, ' ', 0, sizeof(ntp_softwarename)-1);
323 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
324                              (u_char *)ntp_softwarename,
325                              strlen(ntp_softwarename)
326                             );
327     } else {
328 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
329                              (u_char *)"N/A",
330                              3
331                             );
332     }
333     break;
334 
335   }
336 
337 
338   default:
339 	  /* If we cannot get the information we need, we will return a generic error to the SNMP client */
340         return SNMP_ERR_GENERR;
341   }
342 
343   return SNMP_ERR_NOERROR;
344 }
345 
346 
347 int get_ntpEntSoftwareVersion (netsnmp_mib_handler *handler,
348                                netsnmp_handler_registration *reginfo,
349                                netsnmp_agent_request_info *reqinfo,
350                                netsnmp_request_info *requests)
351 {
352 
353    switch (reqinfo->mode) {
354    case MODE_GET:
355    {
356 
357     if ( read_ntp_value("version", ntpvalue, NTPQ_BUFLEN) )
358     {
359 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
360                              (u_char *)ntpvalue,
361                              strlen(ntpvalue)
362                             );
363     } else {
364 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
365                              (u_char *)"N/A",
366                              3
367                             );
368     }
369     break;
370 
371   }
372 
373 
374   default:
375 	  /* If we cannot get the information we need, we will return a generic error to the SNMP client */
376         return SNMP_ERR_GENERR;
377   }
378 
379   return SNMP_ERR_NOERROR;
380 }
381 
382 
383 int get_ntpEntSoftwareVersionVal (netsnmp_mib_handler *handler,
384                                netsnmp_handler_registration *reginfo,
385                                netsnmp_agent_request_info *reqinfo,
386                                netsnmp_request_info *requests)
387 {
388    unsigned int i = 0;
389    switch (reqinfo->mode) {
390    case MODE_GET:
391    {
392 
393     if ( read_ntp_value("versionval", ntpvalue, NTPQ_BUFLEN) )
394     {
395 	i=atoi(ntpvalue);
396 	snmp_set_var_typed_value(requests->requestvb, ASN_UNSIGNED,
397                              (u_char *) &i,
398                              sizeof (i)
399                             );
400     } else {
401 	i = 0;
402 	snmp_set_var_typed_value(requests->requestvb, ASN_UNSIGNED,
403                              (u_char *) &i,
404                              sizeof(i)
405                             );
406     }
407     break;
408 
409   }
410 
411 
412   default:
413 	  /* If we cannot get the information we need, we will return a generic error to the SNMP client */
414         return SNMP_ERR_GENERR;
415   }
416 
417   return SNMP_ERR_NOERROR;
418 }
419 
420 
421 
422 int get_ntpEntSoftwareVendor (netsnmp_mib_handler *handler,
423                                netsnmp_handler_registration *reginfo,
424                                netsnmp_agent_request_info *reqinfo,
425                                netsnmp_request_info *requests)
426 {
427 
428    switch (reqinfo->mode) {
429    case MODE_GET:
430    {
431 
432     if ( read_ntp_value("vendor", ntpvalue, NTPQ_BUFLEN) )
433     {
434 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
435                              (u_char *)ntpvalue,
436                              strlen(ntpvalue)
437                             );
438     } else {
439 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
440                              (u_char *)"N/A",
441                              3
442                             );
443     }
444     break;
445 
446   default:
447 	  /* If we cannot get the information we need, we will return a generic error to the SNMP client */
448         return SNMP_ERR_GENERR;
449    }
450   }
451   return SNMP_ERR_NOERROR;
452 }
453 
454 
455 int get_ntpEntSystemType (netsnmp_mib_handler *handler,
456                                netsnmp_handler_registration *reginfo,
457                                netsnmp_agent_request_info *reqinfo,
458                                netsnmp_request_info *requests)
459 {
460 
461    switch (reqinfo->mode) {
462    case MODE_GET:
463    {
464 
465     if ( read_ntp_value("systemtype", ntpvalue, NTPQ_BUFLEN) )
466     {
467 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
468                              (u_char *)ntpvalue,
469                              strlen(ntpvalue)
470                             );
471     }
472 
473     if ( read_ntp_value("system", ntpvalue, NTPQ_BUFLEN) )
474     {
475 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
476                              (u_char *)ntpvalue,
477                              strlen(ntpvalue)
478                             );
479     } else {
480 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
481                              (u_char *)"N/A",
482                              3
483                             );
484     }
485     break;
486 
487   }
488 
489 
490   default:
491 	  /* If we cannot get the information we need, we will return a generic error to the SNMP client */
492         return SNMP_ERR_GENERR;
493   }
494 
495   return SNMP_ERR_NOERROR;
496 }
497 
498 int get_ntpEntTimeResolution (netsnmp_mib_handler *handler,
499                                netsnmp_handler_registration *reginfo,
500                                netsnmp_agent_request_info *reqinfo,
501                                netsnmp_request_info *requests)
502 {
503 
504    switch (reqinfo->mode) {
505    case MODE_GET:
506    {
507 
508     if ( read_ntp_value("resolution", ntpvalue, NTPQ_BUFLEN) )
509     {
510 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
511                              (u_char *)ntpvalue,
512                              strlen(ntpvalue)
513                             );
514     } else {
515 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
516                              (u_char *)"N/A",
517                              3
518                             );
519     }
520     break;
521 
522   }
523 
524 
525   default:
526 	  /* If we cannot get the information we need, we will return a generic error to the SNMP client */
527         return SNMP_ERR_GENERR;
528   }
529 
530   return SNMP_ERR_NOERROR;
531 }
532 
533 
534 int get_ntpEntTimeResolutionVal (netsnmp_mib_handler *handler,
535                                netsnmp_handler_registration *reginfo,
536                                netsnmp_agent_request_info *reqinfo,
537                                netsnmp_request_info *requests)
538 {
539 
540    unsigned int i = 0;
541    switch (reqinfo->mode) {
542    case MODE_GET:
543    {
544 
545     if ( read_ntp_value("resolutionval", ntpvalue, NTPQ_BUFLEN) )
546     {
547 	i=atoi(ntpvalue);
548 	snmp_set_var_typed_value(requests->requestvb, ASN_UNSIGNED,
549                              (u_char *) &i,
550                              sizeof (i)
551                             );
552     } else {
553 	i = 0;
554 	snmp_set_var_typed_value(requests->requestvb, ASN_UNSIGNED,
555                              (u_char *) &i,
556                              sizeof(i)
557                             );
558     }
559     break;
560 
561   }
562 
563 
564   default:
565 	  /* If we cannot get the information we need, we will return a generic error to the SNMP client */
566         return SNMP_ERR_GENERR;
567   }
568 
569   return SNMP_ERR_NOERROR;
570 }
571 
572 
573 int get_ntpEntTimePrecision (netsnmp_mib_handler *handler,
574                                netsnmp_handler_registration *reginfo,
575                                netsnmp_agent_request_info *reqinfo,
576                                netsnmp_request_info *requests)
577 {
578    switch (reqinfo->mode) {
579    case MODE_GET:
580    {
581 
582     if ( read_ntp_value("precision", ntpvalue, NTPQ_BUFLEN) )
583     {
584 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
585                              (u_char *)ntpvalue,
586                              strlen(ntpvalue)
587                             );
588     } else {
589 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
590                              (u_char *)"N/A",
591                              3
592                             );
593     }
594     break;
595 
596   }
597 
598 
599   default:
600 	  /* If we cannot get the information we need, we will return a generic error to the SNMP client */
601         return SNMP_ERR_GENERR;
602   }
603 
604   return SNMP_ERR_NOERROR;
605 }
606 
607 int get_ntpEntTimePrecisionVal (netsnmp_mib_handler *handler,
608                                netsnmp_handler_registration *reginfo,
609                                netsnmp_agent_request_info *reqinfo,
610                                netsnmp_request_info *requests)
611 {
612 
613     int i = 0;
614    switch (reqinfo->mode) {
615    case MODE_GET:
616    {
617 
618     if ( read_ntp_value("precision", ntpvalue, NTPQ_BUFLEN) )
619     {
620 	i=atoi(ntpvalue);
621 	snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
622                              (u_char *) &i,
623                              sizeof (i)
624                             );
625     } else {
626 	i = 0;
627 	snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
628                              (u_char *) &i,
629                              sizeof(i)
630                             );
631     }
632     break;
633 
634   }
635 
636 
637   default:
638 	  /* If we cannot get the information we need, we will return a generic error to the SNMP client */
639         return SNMP_ERR_GENERR;
640   }
641 
642   return SNMP_ERR_NOERROR;
643 }
644 
645 
646 
647 int get_ntpEntTimeDistance (netsnmp_mib_handler *handler,
648                                netsnmp_handler_registration *reginfo,
649                                netsnmp_agent_request_info *reqinfo,
650                                netsnmp_request_info *requests)
651 {
652    switch (reqinfo->mode) {
653    case MODE_GET:
654    {
655 
656     if ( read_ntp_value("rootdelay", ntpvalue, NTPQ_BUFLEN) )
657     {
658 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
659                              (u_char *)ntpvalue,
660                              strlen(ntpvalue)
661                             );
662     } else {
663 	snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
664                              (u_char *)"N/A",
665                              3
666                             );
667     }
668     break;
669 
670   }
671 
672 
673   default:
674 	  /* If we cannot get the information we need, we will return a generic error to the SNMP client */
675         return SNMP_ERR_GENERR;
676   }
677 
678   return SNMP_ERR_NOERROR;
679 }
680 
681 
682 /*
683  *
684  * Initialize sub agent
685  * TODO: Define NTP MIB OID (has to be assigned by IANA)
686  * At the moment we use a private MIB branch (enterprises.5597.99)
687  */
688 
689 void
690 init_ntpSnmpSubagentObject(void)
691 {
692 
693     /* Register all MIB objects with the agentx master */
694 
695   _SETUP_OID_RO( ntpEntSoftwareName ,  	NTPV4_OID , 1, 1, 1, 0  );
696   _SETUP_OID_RO( ntpEntSoftwareVersion ,  	NTPV4_OID , 1, 1, 2, 0  );
697   _SETUP_OID_RO( ntpEntSoftwareVersionVal ,	NTPV4_OID , 1, 1, 3, 0  );
698   _SETUP_OID_RO( ntpEntSoftwareVendor ,  	NTPV4_OID , 1, 1, 4, 0  );
699   _SETUP_OID_RO( ntpEntSystemType ,  		NTPV4_OID , 1, 1, 5, 0  );
700   _SETUP_OID_RO( ntpEntTimeResolution ,  	NTPV4_OID , 1, 1, 6, 0  );
701   _SETUP_OID_RO( ntpEntTimeResolutionVal , 	NTPV4_OID , 1, 1, 7, 0  );
702   _SETUP_OID_RO( ntpEntTimePrecision ,  	NTPV4_OID , 1, 1, 8, 0  );
703   _SETUP_OID_RO( ntpEntTimePrecisionVal ,  	NTPV4_OID , 1, 1, 9, 0  );
704   _SETUP_OID_RO( ntpEntTimeDistance ,  	NTPV4_OID , 1, 1,10, 0  );
705 
706 }
707 
708