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