1 /*****************************************************************************
2 *
3 * ntpSnmpSubAgentObject.c
4 *
5 * This file provides the callback functions for net-snmp and registers the
6 * serviced MIB objects with the master agent.
7 *
8 * Each object has its own callback function that is called by the
9 * master agent process whenever someone queries the corresponding MIB
10 * object.
11 *
12 * At the moment this triggers a full send/receive procedure for each
13 * queried MIB object, one of the things that are still on my todo list:
14 * a caching mechanism that reduces the number of requests sent to the
15 * ntpd process.
16 *
17 ****************************************************************************/
18 #include <ntp_snmp.h>
19 #include <ctype.h>
20 #include <ntp.h>
21 #include <libntpq.h>
22
23 /* general purpose buffer length definition */
24 #define NTPQ_BUFLEN 2048
25
26 char ntpvalue[NTPQ_BUFLEN];
27
28
29 /*****************************************************************************
30 *
31 * ntpsnmpd_parse_string
32 *
33 * This function will parse a given NULL terminated string and cut it
34 * into a fieldname and a value part (using the '=' as the delimiter.
35 * The fieldname will be converted to uppercase and all whitespace
36 * characters are removed from it.
37 * The value part is stripped, e.g. all whitespace characters are removed
38 * from the beginning and end of the string.
39 * If the value is started and ended with quotes ("), they will be removed
40 * and everything between the quotes is left untouched (including
41 * whitespace)
42 * Example:
43 * server host name = hello world!
44 * will result in a field string "SERVERHOSTNAME" and a value
45 * of "hello world!".
46 * My first Parameter = " is this! "
47 * results in a field string "MYFIRSTPARAMETER" and a value " is this! "
48 ****************************************************************************
49 * Parameters:
50 * string const char * The source string to parse.
51 * NOTE: must be NULL terminated!
52 * field char * The buffer for the field name.
53 * fieldsize size_t The size of the field buffer.
54 * value char * The buffer for the value.
55 * valuesize size_t The size of the value buffer.
56 *
57 * Returns:
58 * size_t length of value string
59 ****************************************************************************/
60
61 size_t
ntpsnmpd_parse_string(const char * string,char * field,size_t fieldsize,char * value,size_t valuesize)62 ntpsnmpd_parse_string(
63 const char * string,
64 char * field,
65 size_t fieldsize,
66 char * value,
67 size_t valuesize
68 )
69 {
70 int i;
71 int j;
72 int loop;
73 size_t str_cnt;
74 size_t val_cnt;
75
76 /* we need at least one byte to work with to simplify */
77 if (fieldsize < 1 || valuesize < 1)
78 return 0;
79
80 str_cnt = strlen(string);
81
82 /* Parsing the field name */
83 j = 0;
84 loop = TRUE;
85 for (i = 0; loop && i <= str_cnt; i++) {
86 switch (string[i]) {
87
88 case '\t': /* Tab */
89 case '\n': /* LF */
90 case '\r': /* CR */
91 case ' ': /* Space */
92 break;
93
94 case '=':
95 loop = FALSE;
96 break;
97
98 default:
99 if (j < fieldsize)
100 field[j++] = toupper(string[i]);
101 }
102 }
103
104 j = min(j, fieldsize - 1);
105 field[j] = '\0';
106
107 /* Now parsing the value */
108 value[0] = '\0';
109 j = 0;
110 for (val_cnt = 0; i < str_cnt; i++) {
111 if (string[i] > 0x0D && string[i] != ' ')
112 val_cnt = min(j + 1, valuesize - 1);
113
114 if (value[0] != '\0' ||
115 (string[i] > 0x0D && string[i] != ' ')) {
116 if (j < valuesize)
117 value[j++] = string[i];
118 }
119 }
120 value[val_cnt] = '\0';
121
122 if (value[0] == '"') {
123 val_cnt--;
124 strlcpy(value, &value[1], valuesize);
125 if (val_cnt > 0 && value[val_cnt - 1] == '"') {
126 val_cnt--;
127 value[val_cnt] = '\0';
128 }
129 }
130
131 return val_cnt;
132 }
133
134
135 /*****************************************************************************
136 *
137 * ntpsnmpd_cut_string
138 *
139 * This function will parse a given NULL terminated string and cut it
140 * into fields using the specified delimiter character.
141 * It will then copy the requested field into a destination buffer
142 * Example:
143 * ntpsnmpd_cut_string(read:my:lips:fool, RESULT, ':', 2, sizeof(RESULT))
144 * will copy "lips" to RESULT.
145 ****************************************************************************
146 * Parameters:
147 * src const char * The name of the source string variable
148 * NOTE: must be NULL terminated!
149 * dest char * The name of the string which takes the
150 * requested field content
151 * delim char The delimiter character
152 * fieldnumber int The number of the required field
153 * (start counting with 0)
154 * maxsize size_t The maximum size of dest
155 *
156 * Returns:
157 * size_t length of resulting dest string
158 ****************************************************************************/
159
160 size_t
ntpsnmpd_cut_string(const char * string,char * dest,char delim,int fieldnumber,size_t maxsize)161 ntpsnmpd_cut_string(
162 const char * string,
163 char * dest,
164 char delim,
165 int fieldnumber,
166 size_t maxsize
167 )
168 {
169 size_t i;
170 size_t j;
171 int l;
172 size_t str_cnt;
173
174 if (maxsize < 1)
175 return 0;
176
177 str_cnt = strlen(string);
178 j = 0;
179 memset(dest, 0, maxsize);
180
181 /* Parsing the field name */
182 for (i = 0, l = 0; i < str_cnt && l <= fieldnumber; i++) {
183 if (string[i] == delim)
184 l++; /* next field */
185 else if (l == fieldnumber && j < maxsize)
186 dest[j++] = string[i];
187 }
188 j = min(j, maxsize - 1);
189 dest[j] = '\0';
190
191 return j;
192 }
193
194
195 /*****************************************************************************
196 *
197 * read_ntp_value
198 *
199 * This function retrieves the value for a given variable, currently
200 * this only supports sysvars. It starts a full mode 6 send/receive/parse
201 * iteration and needs to be optimized, e.g. by using a caching mechanism
202 *
203 ****************************************************************************
204 * Parameters:
205 * variable char* The name of the required variable
206 * rbuffer char* The buffer where the value goes
207 * maxlength int Max. number of bytes for resultbuf
208 *
209 * Returns:
210 * u_int number of chars that have been copied to
211 * rbuffer
212 ****************************************************************************/
213
214 size_t
read_ntp_value(const char * variable,char * value,size_t valuesize)215 read_ntp_value(
216 const char * variable,
217 char * value,
218 size_t valuesize
219 )
220 {
221 size_t sv_len;
222 char sv_data[NTPQ_BUFLEN];
223
224 memset(sv_data, 0, sizeof(sv_data));
225 sv_len = ntpq_read_sysvars(sv_data, sizeof(sv_data));
226
227 if (0 == sv_len)
228 return 0;
229 else
230 return ntpq_getvar(sv_data, sv_len, variable, value,
231 valuesize);
232 }
233
234
235 /*****************************************************************************
236 *
237 * The get_xxx functions
238 *
239 * The following function calls are callback functions that will be
240 * used by the master agent process to retrieve a value for a requested
241 * MIB object.
242 *
243 ****************************************************************************/
244
245
get_ntpEntSoftwareName(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)246 int get_ntpEntSoftwareName (netsnmp_mib_handler *handler,
247 netsnmp_handler_registration *reginfo,
248 netsnmp_agent_request_info *reqinfo,
249 netsnmp_request_info *requests)
250 {
251 char ntp_softwarename[NTPQ_BUFLEN];
252
253 memset (ntp_softwarename, 0, NTPQ_BUFLEN);
254
255 switch (reqinfo->mode) {
256 case MODE_GET:
257 {
258 if ( read_ntp_value("product", ntpvalue, NTPQ_BUFLEN) )
259 {
260 snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
261 (u_char *)ntpvalue,
262 strlen(ntpvalue)
263 );
264 }
265 else if ( read_ntp_value("version", ntpvalue, NTPQ_BUFLEN) )
266 {
267 ntpsnmpd_cut_string(ntpvalue, ntp_softwarename, ' ', 0, sizeof(ntp_softwarename)-1);
268 snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
269 (u_char *)ntp_softwarename,
270 strlen(ntp_softwarename)
271 );
272 } else {
273 snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
274 (u_char *)"N/A",
275 3
276 );
277 }
278 break;
279
280 }
281
282
283 default:
284 /* If we cannot get the information we need, we will return a generic error to the SNMP client */
285 return SNMP_ERR_GENERR;
286 }
287
288 return SNMP_ERR_NOERROR;
289 }
290
291
get_ntpEntSoftwareVersion(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)292 int get_ntpEntSoftwareVersion (netsnmp_mib_handler *handler,
293 netsnmp_handler_registration *reginfo,
294 netsnmp_agent_request_info *reqinfo,
295 netsnmp_request_info *requests)
296 {
297
298 switch (reqinfo->mode) {
299 case MODE_GET:
300 {
301
302 if ( read_ntp_value("version", ntpvalue, NTPQ_BUFLEN) )
303 {
304 snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
305 (u_char *)ntpvalue,
306 strlen(ntpvalue)
307 );
308 } else {
309 snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
310 (u_char *)"N/A",
311 3
312 );
313 }
314 break;
315
316 }
317
318
319 default:
320 /* If we cannot get the information we need, we will return a generic error to the SNMP client */
321 return SNMP_ERR_GENERR;
322 }
323
324 return SNMP_ERR_NOERROR;
325 }
326
327
get_ntpEntSoftwareVendor(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)328 int get_ntpEntSoftwareVendor (netsnmp_mib_handler *handler,
329 netsnmp_handler_registration *reginfo,
330 netsnmp_agent_request_info *reqinfo,
331 netsnmp_request_info *requests)
332 {
333
334 switch (reqinfo->mode) {
335 case MODE_GET:
336 {
337
338 if ( read_ntp_value("vendor", ntpvalue, NTPQ_BUFLEN) )
339 {
340 snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
341 (u_char *)ntpvalue,
342 strlen(ntpvalue)
343 );
344 } else {
345 snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
346 (u_char *)"N/A",
347 3
348 );
349 }
350 break;
351
352 default:
353 /* If we cannot get the information we need, we will return a generic error to the SNMP client */
354 return SNMP_ERR_GENERR;
355 }
356 }
357 return SNMP_ERR_NOERROR;
358 }
359
360
get_ntpEntSystemType(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)361 int get_ntpEntSystemType (netsnmp_mib_handler *handler,
362 netsnmp_handler_registration *reginfo,
363 netsnmp_agent_request_info *reqinfo,
364 netsnmp_request_info *requests)
365 {
366
367 switch (reqinfo->mode) {
368 case MODE_GET:
369 {
370
371 if ( read_ntp_value("systemtype", ntpvalue, NTPQ_BUFLEN) )
372 {
373 snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
374 (u_char *)ntpvalue,
375 strlen(ntpvalue)
376 );
377 }
378
379 if ( read_ntp_value("system", ntpvalue, NTPQ_BUFLEN) )
380 {
381 snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
382 (u_char *)ntpvalue,
383 strlen(ntpvalue)
384 );
385 } else {
386 snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
387 (u_char *)"N/A",
388 3
389 );
390 }
391 break;
392
393 }
394
395
396 default:
397 /* If we cannot get the information we need, we will return a generic error to the SNMP client */
398 return SNMP_ERR_GENERR;
399 }
400
401 return SNMP_ERR_NOERROR;
402 }
403
404
405 /*
406 * ntpEntTimeResolution
407 * "The time resolution in integer format, where the resolution
408 * is represented as divisions of a second, e.g., a value of 1000
409 * translates to 1.0 ms."
410 *
411 * ntpEntTimeResolution is a challenge for ntpd, as the resolution is
412 * not known nor exposed by ntpd, only the measured precision (time to
413 * read the clock).
414 *
415 * Logically the resolution must be at least the precision, so report
416 * it as our best approximation of resolution until/unless ntpd provides
417 * better.
418 */
419 int
get_ntpEntTimeResolution(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)420 get_ntpEntTimeResolution(
421 netsnmp_mib_handler * handler,
422 netsnmp_handler_registration * reginfo,
423 netsnmp_agent_request_info * reqinfo,
424 netsnmp_request_info * requests
425 )
426 {
427 int precision;
428 u_int32 resolution;
429
430 switch (reqinfo->mode) {
431
432 case MODE_GET:
433 if (!read_ntp_value("precision", ntpvalue,
434 sizeof(ntpvalue)))
435 return SNMP_ERR_GENERR;
436 if (1 != sscanf(ntpvalue, "%d", &precision))
437 return SNMP_ERR_GENERR;
438 if (precision >= 0)
439 return SNMP_ERR_GENERR;
440 precision = max(precision, -31);
441 resolution = 1 << -precision;
442 snmp_set_var_typed_value(
443 requests->requestvb,
444 ASN_UNSIGNED,
445 (void *)&resolution,
446 sizeof(resolution));
447 break;
448
449 default:
450 return SNMP_ERR_GENERR;
451 }
452
453 return SNMP_ERR_NOERROR;
454 }
455
456
457 /*
458 * ntpEntTimePrecision
459 * "The entity's precision in integer format, shows the precision.
460 * A value of -5 would mean 2^-5 = 31.25 ms."
461 */
462 int
get_ntpEntTimePrecision(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)463 get_ntpEntTimePrecision(
464 netsnmp_mib_handler * handler,
465 netsnmp_handler_registration * reginfo,
466 netsnmp_agent_request_info * reqinfo,
467 netsnmp_request_info * requests
468 )
469 {
470 int precision;
471 int32 precision32;
472
473 switch (reqinfo->mode) {
474
475 case MODE_GET:
476 if (!read_ntp_value("precision", ntpvalue,
477 sizeof(ntpvalue)))
478 return SNMP_ERR_GENERR;
479 if (1 != sscanf(ntpvalue, "%d", &precision))
480 return SNMP_ERR_GENERR;
481 precision32 = (int32)precision;
482 snmp_set_var_typed_value(
483 requests->requestvb,
484 ASN_INTEGER,
485 (void *)&precision32,
486 sizeof(precision32));
487 break;
488
489 default:
490 return SNMP_ERR_GENERR;
491 }
492
493 return SNMP_ERR_NOERROR;
494 }
495
496
get_ntpEntTimeDistance(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)497 int get_ntpEntTimeDistance (netsnmp_mib_handler *handler,
498 netsnmp_handler_registration *reginfo,
499 netsnmp_agent_request_info *reqinfo,
500 netsnmp_request_info *requests)
501 {
502 switch (reqinfo->mode) {
503 case MODE_GET:
504 {
505
506 if ( read_ntp_value("rootdelay", ntpvalue, NTPQ_BUFLEN) )
507 {
508 snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
509 (u_char *)ntpvalue,
510 strlen(ntpvalue)
511 );
512 } else {
513 snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
514 (u_char *)"N/A",
515 3
516 );
517 }
518 break;
519
520 }
521
522
523 default:
524 /* If we cannot get the information we need, we will return a generic error to the SNMP client */
525 return SNMP_ERR_GENERR;
526 }
527
528 return SNMP_ERR_NOERROR;
529 }
530
531
532 /*
533 *
534 * Initialize sub agent
535 */
536
537 void
init_ntpSnmpSubagentObject(void)538 init_ntpSnmpSubagentObject(void)
539 {
540 /* Register all MIB objects with the agentx master */
541 NTP_OID_RO( ntpEntSoftwareName, 1, 1, 1, 0);
542 NTP_OID_RO( ntpEntSoftwareVersion, 1, 1, 2, 0);
543 NTP_OID_RO( ntpEntSoftwareVendor, 1, 1, 3, 0);
544 NTP_OID_RO( ntpEntSystemType, 1, 1, 4, 0);
545 NTP_OID_RO( ntpEntTimeResolution, 1, 1, 5, 0);
546 NTP_OID_RO( ntpEntTimePrecision, 1, 1, 6, 0);
547 NTP_OID_RO( ntpEntTimeDistance, 1, 1, 7, 0);
548 }
549
550