1<?php 2 3/** 4 * Observium 5 * 6 * This file is part of Observium. 7 * 8 * @package observium 9 * @subpackage snmp 10 * @author Adam Armstrong <adama@observium.org> 11 * @copyright (C) 2006-2013 Adam Armstrong, (C) 2013-2019 Observium Limited 12 * 13 */ 14 15## If anybody has again the idea to implement the PHP internal library calls, 16## be aware that it was tried and banned by lead dev Adam 17## 18## TRUE STORY. THAT SHIT IS WHACK. -- adama. 19 20//CLEANME: 21// snmpwalk_cache_oid_num() - (deprecated) not used anymore 22// snmpget_entity_oids() - (deprecated) not used anymore 23// snmp_cache_slotport_oid() - (deprecated) not used anymore 24// snmp_walk_parser() - (deprecated) used in poller module netscaler-vsvr, need rewrite 25// snmp_walk_parser2() - (deprecated) not used anymore 26// parse_oid() - (deprecated) not used anymore, use snmp_parse_line() 27// parse_oid2() - (deprecated) used in poller/discovery module mac-accounting, need rewrite 28 29// `egrep -r 'snmpwalk_cache_oid *\( *\$' . | grep -v snmp.inc.php | wc -l` => 519 30// `egrep -r 'snmpwalk_cache_multi_oid *\( *\$' . | grep -v snmp.inc.php | wc -l` => 387 31// snmpwalk_cache_multi_oid() - (duplicate) call to snmpwalk_cache_oid() 32 33/** 34 * MIB dirs generate functions 35 */ 36 37/** 38 * Generates a list of mibdirs in the correct format for net-snmp 39 * 40 * @return string 41 * @global array $config 42 * @param array $mibs An array of MIB dirs or a string containing a single MIB dir 43 */ 44function mib_dirs($mibs = array()) 45{ 46 global $config; 47 48 $dirs = array($config['mib_dir'].'/rfc', $config['mib_dir'].'/net-snmp'); 49 50 if (!is_array($mibs)) { $mibs = array($mibs); } 51 52 foreach ($mibs as $mib) 53 { 54 if (ctype_alnum(str_replace(array('-', '_'), '', $mib))) 55 { 56 // If mib name equals 'mibs' just add root mib_dir to list 57 $dirs[] = ($mib == 'mibs' ? $config['mib_dir'] : $config['mib_dir'].'/'.$mib); 58 } 59 } 60 61 return implode(':', array_unique($dirs)); 62} 63 64 65/** 66 * Finds directories for requested MIBs as defined by the MIB definitions. 67 * 68 * @param string $mib One or more MIBs (separated by ':') to return the MIB dir for 69 * 70 * @return string Directories for requested MIBs, separated by ':' (for net-snmp) 71 * 72 */ 73function snmp_mib2mibdirs($mib) 74{ 75 global $config; 76 77 $def_mibdirs = array(); 78 79 // As we accept multiple MIBs separated by :, process them all for definition entries 80 foreach (explode(':', $mib) as $xmib) 81 { 82 if (!empty($config['mibs'][$xmib]['mib_dir'])) // Array or non-empty string 83 { 84 // Add definition based MIB dir. Don't worry about deduplication, mib_dirs() sorts that out for us 85 $def_mibdirs = array_merge($def_mibdirs, (array)$config['mibs'][$xmib]['mib_dir']); 86 } 87 } 88 89/* 90 if (count($def_mibdirs)) 91 { 92 // Use MIB dirs found via foreach above 93 return mib_dirs($def_mibdirs); 94 } else { 95 // No specific MIB dirs found, set default Observium MIB dir 96 return $config['mib_dir']; 97 } 98*/ 99 return mib_dirs($def_mibdirs); // Always return set of mib dirs (prevent Cannot find module (LM-SENSORS-MIB): At line 1 in (none)) 100} 101 102/** 103 * Expand ENTITY mib by vendor type MIB 104 * 105 * @param array $device Device array 106 * @param string $mib List of MIBs, separated by ':' 107 * @return string New list of MIBs expanded by additional MIBs, separated by ':' 108 */ 109function snmp_mib_entity_vendortype($device, $mib) 110{ 111 global $config; 112 113 $mibs = explode(':', $mib); 114 115 if (!in_array('ENTITY-MIB', $mibs)) 116 { 117 // No entity mib in list, return original 118 return $mib; 119 } 120 else if (isset($config['os'][$device['os']]['vendortype_mib'])) 121 { 122 $mibs[] = $config['os'][$device['os']]['vendortype_mib']; 123 } 124 else if (isset($config['os_group']['default']['vendortype_mib'])) 125 { 126 $mibs[] = $config['os_group']['default']['vendortype_mib']; 127 } 128 129 // Reimplode mibs list 130 return implode(':', array_unique($mibs)); 131} 132 133/** 134 * Convert/parse/validate oids & values 135 */ 136 137/** 138 * De-wrap 32bit counters 139 * Crappy function to get workaround 32bit counter wrapping in HOST-RESOURCES-MIB 140 * See: http://blog.logicmonitor.com/2011/06/11/linux-monitoring-net-snmp-and-terabyte-file-systems/ 141 * 142 * @param integer $value 143 * @return integer 144 */ 145function snmp_dewrap32bit($value) 146{ 147 if (is_numeric($value) && $value < 0) 148 { 149 return ($value + 4294967296); 150 } else { 151 return $value; 152 } 153} 154 155/** 156 * Combine High and Low sizes into full 64bit size 157 * Used in UCD-SNMP-MIB and NIMBLE-MIB 158 * Note, this function required 64bit system! 159 * 160 * @param integer $high High bits value 161 * @param integer $low Low bits value 162 * @return integer Result sum 64bit 163 */ 164function snmp_size64_high_low($high, $low) 165{ 166 return $high * 4294967296 + $low; 167 //return $high << 32 + $low; 168} 169 170/** 171 * Clean returned numeric data from snmp output 172 * Supports only non-scientific numbers 173 * Examples: " 20,4" -> 20.4 174 * 175 * @param string $value 176 * @return mixed $numeric 177 */ 178function snmp_fix_numeric($value) 179{ 180 if (is_numeric($value)) { return $value + 0; } // If already numeric just return value 181 else if (!is_string($value)) { return $value; } // Non string values just return as is 182 183 $value = trim($value, " \t\n\r\0\x0B\""); 184 // Possible more derp case: 185 // CPU Temperature-Ctlr B: 58 C 136.40F 186 foreach (explode(': ', $value) as $numeric) 187 { 188 list($numeric) = explode(' ', $numeric); 189 $numeric = preg_replace('/[^0-9a-z\-,\.]/i', '', $numeric); 190 // Some retarded devices report data with spaces and commas: STRING: " 20,4" 191 $numeric = str_replace(',', '.', $numeric); 192 if (is_numeric($numeric)) 193 { 194 // If cleaned data is numeric return number 195 return $numeric + 0; 196 } 197 else if (preg_match('/^(\d+(?:\.\d+)?)[a-z]+$/i', $numeric, $matches)) 198 { 199 // Number with unit, ie "8232W" 200 return $matches[1] + 0; 201 } 202 } 203 204 // Else return original value 205 return $value; 206} 207 208/** 209 * Fixed ascii coded chars in snmp string as correct UTF-8 chars. 210 * Convert all Mac/Windows newline chars (\r\n, \r) to Unix char (\n) 211 * 212 * NOTE, currently support only one-byte unicode 213 * 214 * Examples: "This is a čא test' ú" -> "This is a čא test' ú" 215 * "P<FA>lt stj<F3>rnst<F6><F0>" -> "Púlt stjórnstöð" 216 * 217 * @param string $string 218 * @return string $string 219 */ 220function snmp_fix_string($string) 221{ 222 if (!preg_match('/^[[:print:]\p{L}]*$/mu', $string)) 223 { 224 // find unprintable and all unicode chars, because old pcre library not always detect orb 225 $debug_msg = '>>> Founded unprintable chars in string:' . PHP_EOL . $string; 226 $string = preg_replace_callback('/[^[:print:]\x00-\x1F\x80-\x9F]/m', 'convert_ord_char', $string); 227 print_debug($debug_msg . PHP_EOL . '>>> Converted to:' . PHP_EOL . $string . PHP_EOL); 228 } 229 230 // Convert all Mac/Windows newline chars (\r\n, \r) to Unix char (\n) 231 $string = nl2nl($string); 232 233 return $string; 234} 235 236/** 237 * Convert an SNMP hex string to regular string 238 * 239 * @param string $string HEX string 240 * @param string $eol Symbol used as EOL (hex 00), default is \n, but last EOL removed 241 * @return string 242 */ 243function snmp_hexstring($string, $eol = "\n") 244{ 245 if (isHexString($string)) 246 { 247 $ascii = hex2str($string, $eol); 248 // clear last EOL CHAR 249 return rtrim($ascii, $eol); 250 } else { 251 return $string; 252 } 253} 254 255/** 256 * Clean SNMP value, ie: trim quotes, spaces, remove "wrong type", fix incorrect UTF8 strings, etc 257 * @param string $value Value 258 * @param integer $flags OBS_SNMP_* flags 259 * @return string Cleaned value 260 */ 261function snmp_value_clean($value, $flags = OBS_SNMP_ALL) 262{ 263 // For null just return NULL 264 if (NULL === $value) 265 { 266 return $value; 267 } 268 269 // Clean quotes and trim 270 $value = trim_quotes($value, $flags); 271 272 // Remove Wrong Type string 273 if (strpos($value, 'Wrong Type') === 0) 274 { 275 $value = preg_replace('/Wrong Type .*?: (.*)/s', '\1', $value); 276 } 277 278 // Fix incorrect UTF8 strings 279 if (is_flag_set(OBS_DECODE_UTF8, $flags)) 280 { 281 $value = snmp_fix_string($value); 282 } 283 284 /* Need use case 285 // Convert incorrect HEX strings back to string 286 if (!is_flag_set(OBS_SNMP_HEX, $flags)) 287 { 288 $value = snmp_hexstring($value); 289 } 290 */ 291 292 return $value; 293} 294 295/** 296 * Convert an SNMP index string (with len!) to regular string 297 * Opposite function for snmp_string_to_oid() 298 * Example: 299 * 9.79.98.115.101.114.118.105.117.109 -> Observium 300 * 301 * @param string $index 302 * @return string 303 */ 304function snmp_oid_to_string($index) 305{ 306 $index = (string)$index; 307 if ($index === '0') { return ''; } // This is just empty string! 308 309 if (preg_match('/^\.?(\d+(?:\.\d+)+)$/', $index, $matches)) 310 { 311 $str_parts = explode('.', $matches[1]); 312 $str_len = array_shift($str_parts); 313 if ($str_len != count($str_parts)) 314 { 315 // break, incorrect index string (str len not match) 316 return $index; 317 } 318 $string = ''; 319 foreach ($str_parts as $char) 320 { 321 if ($char > 255) 322 { 323 // break, incorrect index string 324 return $index; 325 } 326 $string .= zeropad(dechex($char)); 327 } 328 return hex2str($string); 329 } 330 331 return $index; 332} 333 334/** 335 * Convert ASCII string to an SNMP index (with len!) 336 * Opposite function for snmp_oid_to_string() 337 * Example: 338 * Observium -> 9.79.98.115.101.114.118.105.117.109 339 * 340 * @param string $string 341 * @return string 342 */ 343function snmp_string_to_oid($string) 344{ 345 // uses the first octet of index as length 346 $index = strlen((string)$string); 347 if ($index === 0) 348 { 349 // Empty string 350 return (string)$index; 351 } 352 353 // converts the index as string to decimal ascii codes 354 foreach (str_split($string) as $char) 355 { 356 $index .= '.' . ord($char); 357 } 358 359 return $index; 360} 361 362/** 363 * Check if returned snmp value is valid 364 * 365 * @param string $value 366 * @return bool 367 */ 368function is_valid_snmp_value($value) 369{ 370 $valid = strpos($value, 'at this OID') === FALSE && 371 strpos($value, 'No more variables left') === FALSE && 372 $value != 'NULL' && $value != 'null' && $value !== NULL; 373 374 return $valid; 375} 376 377/** 378 * Parse each line in output from snmpwalk into: 379 * oid (raw), oid_name, index, index_parts, index_count, value 380 * 381 * This parser always used snmpwalk with base options: -OQUs 382 * and allowed to use additional options: bnexX 383 * 384 * Value always cleaned from unnecessary data by snmp_value_clean() 385 * 386 * @param string $line snmpwalk output line 387 * @param integer $flags Common snmp flags 388 * @return array Array with parsed values 389 */ 390function snmp_parse_line($line, $flags = OBS_SNMP_ALL) 391{ 392 /** 393 * Note, this is parse line only for -OQUs (and additionally: bnexX) 394 * Q - Removes the type information when displaying varbind values: SNMPv2-MIB::sysUpTime.0 = 1:15:09:27.63 395 * U - Do not print the UNITS suffix at the end of the value 396 * s - Do not display the name of the MIB 397 * b - Display table indexes numerically: SNMP-VIEW-BASED-ACM-MIB::vacmSecurityModel.0.3.119.101.115 = xxx 398 * n - Displays the OID numerically: .1.3.6.1.2.1.1.3.0 = Timeticks: (14096763) 1 day, 15:09:27.63 399 * e - Removes the symbolic labels from enumeration values: forwarding(1) -> 1 400 * x - Force display string values as Hex strings 401 * X - Display table indexes in a more "program like" output: IPv6-MIB::ipv6RouteIfIndex[3ffe:100:ff00:0:0:0:0:0][64][1] = 2 402 */ 403 404 list($r_oid, $value) = explode(' =', $line, 2); 405 $r_oid = trim($r_oid); 406 $value = snmp_value_clean($value, $flags); 407 408 $array = array('oid' => $r_oid, 409 'value' => $value); 410 411 if (is_flag_set(OBS_SNMP_NUMERIC, $flags)) 412 { 413 // For numeric, just return raw oid and value 414 // Example: .1.3.6.1.2.1.1.3.0 = 15:09:27.63 415 if (isset($r_oid[0])) 416 { 417 // I think not possible, but I leave this here, just in case --mike 418 //if ($r_oid[0] !== '.') 419 //{ 420 // $array['index'] = '.' . $array['index']; 421 //} 422 $array['index_count'] = 1; 423 } else { 424 $array['index_count'] = 0; 425 } 426 $array['index'] = $r_oid; 427 return $array; 428 } 429 430 if ($is_table = is_flag_set(OBS_SNMP_TABLE, $flags)) 431 { 432 // For table use another parse rules 433 // Example: ipv6RouteIfIndex[3ffe:100:ff00:0:0:0:0:0][64][1] 434 // also mixed index, ie wcAccessPointMac[6:20:c:c8:39:b].96 435 // wcAccessPointMac[6:20:c:c8:39:b]."sdf sdkfps" 436 //if (preg_match('/^(\S+?)((?:\[.+?\])+)((?:\.\d+)+)?/', $r_oid, $matches)) 437 if (preg_match('/^(\S+?)((?:\[.+?\])+)((?:\.(?:\d+|"[^"]*"))+)?/', $r_oid, $matches)) 438 { 439 $oid_parts = explode('][', trim($matches[2], '[]')); // square brackets part 440 array_unshift($oid_parts, $matches[1]); // Oid name 441 if (isset($matches[3])) // Numeric part (if exist) 442 { 443 foreach (explode('.', ltrim($matches[3], '.')) as $oid_part) 444 { 445 $oid_parts[] = trim($oid_part, '"'); 446 } 447 } 448 } else { 449 // Incorrect? 450 $oid_parts = array(); 451 } 452 //foreach (explode('[', $r_oid) as $oid_part) 453 //{ 454 // $oid_parts[] = rtrim($oid_part, ']'); 455 //} 456 } 457 else if (strpos($r_oid, '"') !== FALSE) 458 { 459 // Example: jnxVpnPwLocalSiteId.l2Circuit."ge-0/1/1.0".621 460 //$oid_part = stripslashes($r_oid); 461 $oid_part = $r_oid; 462 $oid_parts = array(); 463 do 464 { 465 if (preg_match('/^"([^"]*)"(?:\.(.+))?/', $oid_part, $matches)) 466 { 467 // Part with stripes 468 $oid_parts[] = $matches[1]; 469 $oid_part = $matches[2]; // Next part 470 } else { 471 $matches = explode('.', $oid_part, 2); 472 $oid_parts[] = $matches[0]; 473 $oid_part = $matches[1]; // Next part 474 } 475 // print_vars($matches); 476 } while (strlen($oid_part) > 0); 477 // print_vars($oid_parts); 478 } else { 479 // Simple, not always correct 480 // Example: vacmSecurityModel.0.3.119.101.115 481 $oid_parts = explode('.', $r_oid); 482 } 483 $array['oid_name'] = array_shift($oid_parts); 484 //$array['oid_name'] = end(explode('::', $array['oid_name'], 2)); // We use -Os 485 $array['index_parts'] = $oid_parts; 486 $array['index_count'] = count($oid_parts); 487 $array['index'] = implode('.', $oid_parts); 488 //var_dump($array); 489 return $array; 490} 491 492// Translate OID string to numeric: 493//'BGP4-V2-MIB-JUNIPER::jnxBgpM2PeerRemoteAs' -> '.1.3.6.1.4.1.2636.5.1.1.2.1.1.1.13' 494// or numeric OID to string: 495// '.1.3.6.1.4.1.9.1.685' -> 'ciscoAIRAP1240' 496// DOCME needs phpdoc block 497// TESTME needs unit testing 498function snmp_translate($oid, $mib = NULL, $mibdir = NULL) 499{ 500 // $rewrite_oids set in rewrites.inc.php 501 global $config; 502 503 if (is_numeric(str_replace('.', '', $oid))) 504 { 505 $options = '-Os'; 506 } 507 else if ($mib) 508 { 509 if (isset($config['mibs'][$mib]['translate'][$oid])) 510 { 511 print_debug("SNMP TRANSLATE (REWRITE): '$mib::$oid' -> '".$config['mibs'][$mib]['translate'][$oid]."'"); 512 return $config['mibs'][$mib]['translate'][$oid]; 513 } 514 515 $oid = $mib . '::' . $oid; 516 } 517 518 $cmd = $config['snmptranslate']; 519 if (isset($options)) { $cmd .= ' ' . $options; } else { $cmd .= ' -On'; } 520 521 // Hardcode ignoring underscore parsing errors because net-snmp is dumb as a bag of rocks 522 // -Pu Toggles whether to allow the underline character in MIB object names and other symbols. 523 // Strictly speaking, this is not valid SMI syntax, but some vendor MIB files define such names. 524 $cmd .= ' -Pu'; 525 526 if ($mib) { $cmd .= ' -m ' . $mib; } 527 528 // Set correct MIB directories based on passed dirs and OS definition 529 // If $mibdir variable is passed to the function, we use it directly 530 if ($mibdir) 531 { 532 $cmd .= " -M $mibdir"; 533 } else { 534 $cmd .= ' -M ' . snmp_mib2mibdirs($mib); 535 } 536 537 $cmd .= ' \'' . $oid . '\''; 538 if (!OBS_DEBUG) { $cmd .= ' 2>/dev/null'; } 539 540 $data = trim(external_exec($cmd)); 541 542 $GLOBALS['snmp_stats']['snmptranslate']['count']++; 543 $GLOBALS['snmp_stats']['snmptranslate']['time'] += $GLOBALS['exec_status']['runtime']; 544 545 546 if ($data && !strstr($data, 'Unknown')) 547 { 548 print_debug("SNMP TRANSLATE (CMD): '$oid' -> '".$data."'"); 549 return $data; 550 } else { 551 return ''; 552 } 553} 554 555 556/** 557 * Common SNMP functions for generate cmd and log errors 558 */ 559 560/** 561 * Build a commandline string for net-snmp commands. 562 * 563 * @param string $command 564 * @param array $device 565 * @param string $oids 566 * @param string $options 567 * @param string $mib 568 * @param string $mibdir Optional, correct path should be set in the MIB definition 569 * @param integer $flags 570 * @global array config 571 * @global array debug 572 * @return string 573 */ 574// TESTME needs unit testing 575function snmp_command($command, $device, $oids, $options, $mib = NULL, &$mibdir = NULL, $flags = OBS_SNMP_ALL) 576{ 577 global $config, $cache; 578 579 get_model_array($device); // Pre-cache model options (if required) 580 581 $nobulk = $device['snmp_version'] == 'v1' || // v1 not support snmp bulk 582 (isset($device['snmp_nobulk']) && $device['snmp_nobulk']) || // device specific option 583 (isset($cache['devices']['model'][$device['device_id']]['snmp']['nobulk']) && 584 $cache['devices']['model'][$device['device_id']]['snmp']['nobulk']) || // device model specific definition 585 (isset($config['os'][$device['os']]['snmp']['nobulk']) && 586 $config['os'][$device['os']]['snmp']['nobulk']); // os specific definition 587 588 // Get the full command path from the config. Choice between bulkwalk and walk. Add max-reps if needed. 589 switch($command) 590 { 591 case 'snmpwalk': 592 if ($nobulk) 593 { 594 $cmd = $config['snmpwalk']; 595 } else { 596 $cmd = $config['snmpbulkwalk']; 597 if (is_numeric($device['snmp_maxrep'])) 598 { 599 // Device specific 600 $cmd .= ' -Cr'.escapeshellarg($device['snmp_maxrep']); 601 } 602 elseif ($config['snmp']['max-rep'] && isset($cache['devices']['model'][$device['device_id']]['snmp']['max-rep'])) 603 { 604 // Device model specific 605 if (is_numeric($cache['devices']['model'][$device['device_id']]['snmp']['max-rep'])) 606 { 607 // Model specific can be FALSE 608 $cmd .= ' -Cr'.escapeshellarg($cache['devices']['model'][$device['device_id']]['snmp']['max-rep']); 609 } 610 } 611 elseif ($config['snmp']['max-rep'] && is_numeric($config['os'][$device['os']]['snmp']['max-rep'])) 612 { 613 // OS specific 614 $cmd .= ' -Cr'.escapeshellarg($config['os'][$device['os']]['snmp']['max-rep']); 615 } 616 } 617 // do not check returned OIDs are increasing 618 if (isset($cache['devices']['model'][$device['device_id']]['snmp']['noincrease'])) 619 { 620 // Device model specific, can be FALSE 621 if ($cache['devices']['model'][$device['device_id']]['snmp']['noincrease']) 622 { 623 $cmd .= ' -Cc'; 624 } 625 } 626 elseif (isset($config['os'][$device['os']]['snmp']['noincrease']) && $config['os'][$device['os']]['snmp']['noincrease']) 627 { 628 // OS specific 629 $cmd .= ' -Cc'; 630 } 631 break; 632 case 'snmpget': 633 case 'snmpgetnext': 634 $cmd = $config[$command]; 635 break; 636 case 'snmpbulkget': 637 // NOTE. Currently not used by us 638 if ($nobulk) 639 { 640 $cmd = $config['snmpget']; 641 } else { 642 $cmd = $config['snmpbulkget']; 643 // NOTE, for snmpbulkget max-repetitions work different than for snmpbulkwalk, 644 // it's returned exactly number (max as possible) _next_ Oid entries. 645 // Default in net-snmp is 10, this can cause troubles if passed oids more than 10 646 if (is_numeric($device['snmp_maxrep'])) 647 { 648 // Device specific 649 $cmd .= ' -Cr'.escapeshellarg($device['snmp_maxrep']); 650 } 651 elseif ($config['snmp']['max-rep'] && isset($cache['devices']['model'][$device['device_id']]['snmp']['max-rep'])) 652 { 653 // Device model specific 654 if (is_numeric($cache['devices']['model'][$device['device_id']]['snmp']['max-rep'])) 655 { 656 // Model specific can be FALSE 657 $cmd .= ' -Cr'.escapeshellarg($cache['devices']['model'][$device['device_id']]['snmp']['max-rep']); 658 } 659 } 660 elseif ($config['snmp']['max-rep'] && is_numeric($config['os'][$device['os']]['snmp']['max-rep'])) 661 { 662 // OS specific 663 $cmd .= ' -Cr'.escapeshellarg($config['os'][$device['os']]['snmp']['max-rep']); 664 } 665 } 666 break; 667 default: 668 print_error("Unknown command $command passed to snmp_command(). THIS SHOULD NOT HAPPEN. PLEASE REPORT TO DEVELOPERS."); 669 return FALSE; 670 } 671 672 // Set timeout values if set in the database, otherwise set to configured defaults 673 if (is_numeric($device['snmp_timeout']) && $device['snmp_timeout'] > 0) 674 { 675 $snmp_timeout = $device['snmp_timeout']; 676 } else if (isset($config['snmp']['timeout'])) { 677 $snmp_timeout = $config['snmp']['timeout']; 678 } 679 680 if (isset($snmp_timeout)) { $cmd .= ' -t ' . escapeshellarg($snmp_timeout); } 681 682 // Set retries if set in the database, otherwise set to configured defaults 683 if (is_numeric($device['snmp_retries']) && $device['snmp_retries'] >= 0) 684 { 685 $snmp_retries = $device['snmp_retries']; 686 } 687 else if (isset($config['snmp']['retries'])) 688 { 689 $snmp_retries = $config['snmp']['retries']; 690 } 691 if (isset($snmp_retries)) { $cmd .= ' -r ' . escapeshellarg($snmp_retries); } 692 693 // If no specific transport is set for the device, default to UDP. 694 if (empty($device['snmp_transport'])) { $device['snmp_transport'] = 'udp'; } 695 696 // If no specific port is set for the device, default to 161. 697 if (!$device['snmp_port']) { $device['snmp_port'] = 161; } 698 699 // Add the SNMP authentication settings for the device 700 $cmd .= snmp_gen_auth($device); 701 702 // Hardcode ignoring underscore parsing errors because net-snmp is dumb as a bag of rocks 703 // -Pu Toggles whether to allow the underline character in MIB object names and other symbols. 704 // Strictly speaking, this is not valid SMI syntax, but some vendor MIB files define such names. 705 // -Pd Disables the loading of MIB object DESCRIPTIONs when parsing MIB files. 706 // This reduces the amount of memory used by the running application. 707 $cmd .= ' -Pud'; 708 709 // Disables the use of DISPLAY-HINT information when assigning values. 710 if (is_flag_set(OBS_SNMP_HEX | OBS_SNMP_DISPLAY_HINT, $flags)) { $cmd .= ' -Ih'; } 711 712 if ($options) { $cmd .= ' ' . $options; } 713 if ($mib) { $cmd .= ' -m ' . $mib; } 714 715 // Set correct MIB directories based on passed dirs and OS definition 716 // If $mibdir variable is passed, we use it directly 717 if (empty($mibdir)) 718 { 719 // Change to correct mibdir, required for store in snmp_errors 720 $mibdir = snmp_mib2mibdirs($mib); 721 } 722 $cmd .= " -M $mibdir"; 723 724 // Add the device URI to the string 725 $cmd .= ' ' . escapeshellarg($device['snmp_transport']).':'.escapeshellarg($device['hostname']).':'.escapeshellarg($device['snmp_port']); 726 727 // Add the OID(s) to the string 728 $oids = trim($oids); 729 if ($oids === '') 730 { 731 print_error("Empty oids passed to snmp_command(). THIS SHOULD NOT HAPPEN. PLEASE REPORT TO DEVELOPERS."); 732 $GLOBALS['snmp_command'] = $cmd; 733 return FALSE; 734 } else { 735 $cmd .= ' ' . addslashes($oids); // Quote slashes for string indexes 736 $GLOBALS['snmp_command'] = $cmd; 737 } 738 739 // If we're not debugging, direct errors to /dev/null. 740 if (!OBS_DEBUG) { $cmd .= ' 2>/dev/null'; } 741 742 return $cmd; 743} 744 745/** 746 * Build authentication for net-snmp commands using device array 747 * 748 * @return string 749 * @param array $device 750 */ 751// TESTME needs unit testing 752function snmp_gen_auth(&$device) 753{ 754 $cmd = ''; 755 756 switch ($device['snmp_version']) 757 { 758 case 'v3': 759 $cmd = ' -v3 -l ' . escapeshellarg($device['snmp_authlevel']); 760 /* NOTE. 761 * For proper work of 'vlan-' context on cisco, it is necessary to add 'match prefix' in snmp-server config --mike 762 * example: snmp-server group MONITOR v3 auth match prefix access SNMP-MONITOR 763 */ 764 $cmd .= ' -n ' . escapeshellarg($device['snmp_context']); // Some devices, like HP, always require option '-n' 765 766 switch ($device['snmp_authlevel']) 767 { 768 case 'authPriv': 769 $cmd .= ' -x ' . escapeshellarg($device['snmp_cryptoalgo']); 770 $cmd .= ' -X ' . escapeshellarg($device['snmp_cryptopass']); 771 // no break here 772 case 'authNoPriv': 773 $cmd .= ' -a ' . escapeshellarg($device['snmp_authalgo']); 774 $cmd .= ' -A ' . escapeshellarg($device['snmp_authpass']); 775 $cmd .= ' -u ' . escapeshellarg($device['snmp_authname']); 776 break; 777 case 'noAuthNoPriv': 778 // We have to provide a username anyway (see Net-SNMP doc) 779 $cmd .= ' -u observium'; 780 break; 781 default: 782 print_error('ERROR: Unsupported SNMPv3 snmp_authlevel (' . $device['snmp_authlevel'] . ')'); 783 } 784 break; 785 786 case 'v2c': 787 case 'v1': 788 $cmd = ' -' . $device['snmp_version']; 789 790 if (isset($device['snmp_context']) && strlen($device['snmp_context'])) 791 { 792 // Commonly used by Vlan based Cisco community 793 $cmd .= ' -c ' . escapeshellarg($device['snmp_community'] . '@' . $device['snmp_context']); 794 } else { 795 $cmd .= ' -c ' . escapeshellarg($device['snmp_community']); 796 } 797 798 break; 799 default: 800 print_error('ERROR: ' . $device['snmp_version'] . ' : Unsupported SNMP Version.'); 801 } 802 803 if (OBS_DEBUG === 1 && !$GLOBALS['config']['snmp']['hide_auth']) 804 { 805 $debug_auth = "DEBUG: SNMP Auth options = $cmd"; 806 print_debug($debug_auth); 807 } 808 809 return $cmd; 810} 811 812/** 813 * Generate common snmp output options (-O) 814 * 815 * @param string $command SNMP command 816 * @param integer $flags SNMP flags 817 * @return string Options string -Oxxx 818 */ 819function snmp_gen_options($command = 'snmpwalk', $flags = 0) 820{ 821 822 // Basic options, 823 // NOTE: 's' has no effect with 'v', 824 // 'Q' better than 'q' (no more Wrong Type): 825 switch($command) 826 { 827 case 'snmpwalk': 828 // walk require output with Oid.index = value 829 $output = 'QUs'; 830 break; 831 case 'snmpget': 832 // get need only varbind values (without Oid) 833 $output = 'QUv'; 834 break; 835 } 836 837 if (is_flag_set(OBS_SNMP_NUMERIC_INDEX, $flags)) { $output .= 'b'; } 838 if (is_flag_set(OBS_SNMP_NUMERIC, $flags)) { $output .= 'n'; } 839 if (is_flag_set(OBS_SNMP_ENUM, $flags)) { $output .= 'e'; } 840 if (is_flag_set(OBS_SNMP_HEX, $flags)) { $output .= 'x'; } 841 if (is_flag_set(OBS_SNMP_ASCII, $flags)) { $output .= 'a'; } 842 if (is_flag_set(OBS_SNMP_TABLE, $flags)) { $output .= 'X'; } 843 if (is_flag_set(OBS_SNMP_TIMETICKS, $flags)) { $output .= 't'; } // Display TimeTicks values as raw numbers: SNMPv2-MIB::sysUpTime.0 = 14096763 844 845 return "-O$output"; 846} 847 848/** 849 * Detect SNMP errors and log it in DB. 850 * Error logged in poller modules only, all other just return error code 851 * 852 * @param string $command Used snmp command (ie: snmpget, snmpwalk) 853 * @param array $device Device array (device_id not allowed) 854 * @param string $oid SNMP oid string 855 * @param string $options SNMP options 856 * @param string $mib SNMP MIBs list 857 * @param string $mibdir SNMP MIB dirs list 858 * @return int Numeric error code. Full list error codes see in definitions: $config['snmp']['errorcodes'] 859 */ 860function snmp_log_errors($command, $device, $oid, $options, $mib, $mibdir) 861{ 862 $error_timestamp = time(); // current timestamp 863 $error_codes = $GLOBALS['config']['snmp']['errorcodes']; 864 $error_code = 0; // By default - OK 865 866 if ($GLOBALS['snmp_status'] === FALSE) 867 { 868 $error_code = 999; // Default Unknown error 869 if (is_array($oid)) 870 { 871 $oid = implode(' ', $oid); 872 } 873 if ($mib == 'SNMPv2-MIB') 874 { 875 // Pre-check for net-snmp errors 876 if ($GLOBALS['exec_status']['exitcode'] === 1) 877 { 878 if (strpos($GLOBALS['exec_status']['stderr'], '.index are too large') !== FALSE) 879 { 880 $error_code = 997; 881 } 882 elseif (preg_match('/(?:Cannot find module|Unknown Object Identifier)/', $GLOBALS['exec_status']['stderr'])) 883 { 884 $error_code = 996; 885 } 886 } 887 if ($error_code === 999) 888 { 889 if ($oid == 'sysObjectID.0 sysUpTime.0') 890 { 891 // this is isSNMPable test, ignore 892 $error_code = 900; 893 } 894 elseif (isset($GLOBALS['config']['os'][$device['os']]['snmpable']) && 895 in_array($oid, $GLOBALS['config']['os'][$device['os']]['snmpable'])) 896 { 897 $error_code = 900; // This is also isSNMPable, ignore 898 } 899 } 900 } 901 902 if ($error_code === 999 && 903 strlen(trim($GLOBALS['exec_status']['stdout'], " \t\n\r\0\x0B\"")) === 0) // Empty or "" 904 { 905 $error_code = 1; // Empty output non critical 906 if ($GLOBALS['exec_status']['exitcode'] === 1 || $GLOBALS['exec_status']['exitcode'] === -1) 907 { 908 $error_code = 1002; 909 if (strpos($GLOBALS['exec_status']['stderr'], '.index are too large') !== FALSE) { $error_code = 997; } 910 elseif (str_contains($GLOBALS['exec_status']['stderr'], array('Cannot find module', 'Unknown Object Identifier'))) { $error_code = 996; } 911 elseif (strpos($GLOBALS['exec_status']['stderr'], 'Empty command passed') !== FALSE) { $error_code = 995; } 912 elseif (str_contains($GLOBALS['exec_status']['stderr'], array('Unknown user name', 'Error: passphrase', 'Unsupported security level'))) 913 { 914 // SNMP v3 auth error 915 $error_code = 991; 916 } 917 } 918 elseif ($GLOBALS['exec_status']['exitcode'] === 2) 919 { 920 // Reason: (noSuchName) There is no such variable name in this MIB. 921 // This is incorrect snmp version used for MIB/oid (mostly snmp v1) 922 $error_code = 1000; 923 } 924 } 925 elseif ($error_code === 999) 926 { 927 if ($GLOBALS['exec_status']['exitcode'] === 2 && strpos($GLOBALS['exec_status']['stderr'], 'Response message would have been too large') !== FALSE) 928 { 929 // "Reason: (tooBig) Response message would have been too large." 930 // Too big max-rep definition used, 931 // this is not exactly device or net-snmp error, just need to set less max-rep in os definition 932 $error_code = 4; 933 } 934 // Non empty output, some errors can ignored 935 elseif (preg_match('/(?:No Such Instance|No Such Object|There is no such variable|No more variables left|Wrong Type)/i', $GLOBALS['exec_status']['stdout']) || 936 $GLOBALS['exec_status']['stdout'] == 'NULL') 937 { 938 $error_code = 1000; 939 } 940 elseif (stripos($GLOBALS['exec_status']['stdout'], 'Authentication failure') !== FALSE) 941 { 942 $error_code = 991; 943 } 944 elseif ($GLOBALS['exec_status']['exitcode'] === 2 || stripos($GLOBALS['exec_status']['stderr'], 'Timeout') !== FALSE) 945 { 946 // non critical 947 $error_code = 2; 948 } 949 elseif ($GLOBALS['exec_status']['exitcode'] === 1) 950 { 951 // Calculate current snmp timeout 952 if (is_numeric($device['snmp_timeout']) && $device['snmp_timeout'] > 0) 953 { 954 $snmp_timeout = $device['snmp_timeout']; 955 } 956 elseif (isset($GLOBALS['config']['snmp']['timeout'])) 957 { 958 $snmp_timeout = $GLOBALS['config']['snmp']['timeout']; 959 } else { 960 $snmp_timeout = 1; 961 } 962 if (is_numeric($device['snmp_retries']) && $device['snmp_retries'] >= 0) 963 { 964 $snmp_retries = $device['snmp_retries']; 965 } 966 elseif (isset($GLOBALS['config']['snmp']['retries'])) 967 { 968 $snmp_retries = $GLOBALS['config']['snmp']['retries']; 969 } else { 970 $snmp_retries = 5; 971 } 972 $runtime_timeout = $snmp_timeout * (1 + $snmp_retries); 973 974 //$error_code = 2; // All other is incomplete request or timeout? 975 if (strpos($GLOBALS['exec_status']['stderr'], '.index are too large') !== FALSE) { $error_code = 997; } 976 elseif (preg_match('/(?:Cannot find module|Unknown Object Identifier)/', $GLOBALS['exec_status']['stderr'])) { $error_code = 996; } 977 elseif (preg_match('/ NULL\Z/', $GLOBALS['exec_status']['stdout'])) { $error_code = 1000; } // NULL as value at end of walk output 978 elseif ($GLOBALS['exec_status']['runtime'] >= $runtime_timeout) { $error_code = 3; } 979 } 980 } 981 982 // Count errors stats 983 $GLOBALS['snmp_stats']['errors'][$command]['count']++; 984 $GLOBALS['snmp_stats']['errors'][$command]['time'] += $GLOBALS['exec_status']['runtime']; 985 986 $msg = 'device: ' . $device['device_id'] . ', cmd: ' . $command . ', options: ' . $options; 987 $msg .= ', mib: \'' . $mib . '\', oid: \'' . $oid . '\''; 988 $msg .= ', cmd exitcode: ' . $GLOBALS['exec_status']['exitcode'] . ',' . PHP_EOL; 989 $msg .= ' snmp error code: #' . $error_code . ', reason: \'' . $error_codes[$error_code]['reason'] . '\', runtime: ' . $GLOBALS['exec_status']['runtime']; 990 991 if (OBS_DEBUG > 0) 992 { 993 if (OBS_DEBUG > 1) 994 { 995 // Show full error 996 print_debug('SNMP ERROR - '. $msg); 997 } 998 elseif ($error_code != 0 && $error_code != 900) 999 { 1000 // Show only common error info 1001 print_message('SNMP ERROR[%r#' . $error_code . ' - ' . $error_codes[$error_code]['reason'] . '%n]', 'color'); 1002 } 1003 } 1004 1005 // Log error into DB, but only in poller modules, all other just return error code 1006 if (isset($GLOBALS['argv'][0]) && in_array(basename($GLOBALS['argv'][0]), array('poller.php'))) 1007 { 1008 if ($error_code > 999 || $error_code < 900) 1009 { 1010 // Count critical errors into DB (only for poller) 1011 $sql = 'SELECT * FROM `snmp_errors` '; 1012 // Note, snmp_options not in unique db index 1013 //$sql .= 'WHERE `device_id` = ? AND `error_code` = ? AND `snmp_cmd` = ? AND `snmp_options` = ? AND `mib` = ? AND `oid` = ?;'; 1014 //$error_db = dbFetchRow($sql, array($device['device_id'], $error_code, $command, $options, $mib, $oid)); 1015 $sql .= 'WHERE `device_id` = ? AND `error_code` = ? AND `snmp_cmd` = ? AND `mib` = ? AND `oid` = ?;'; 1016 $error_db = dbFetchRow($sql, array($device['device_id'], $error_code, $command, $mib, $oid)); 1017 if (isset($error_db['error_id'])) 1018 { 1019 $error_db['error_count']++; 1020 1021 // DEBUG, error rate, if error rate >= 0.95, than error appears in each poll run 1022 //$poll_count = round(($error_timestamp - $error_db['added']) / $poll_period) + 1; 1023 //$error_db['error_rate'] = $error_db['error_count'] / $poll_count; 1024 //$msg .= ', rate: ' . $error_db['error_rate'] . ' err/poll'; 1025 //logfile('snmp.log', $msg); 1026 1027 // Update count 1028 $update_array = array('error_count' => $error_db['error_count'], 1029 'updated' => $error_timestamp); 1030 if ($error_db['mib_dir'] != $mibdir) { $update_array['mib_dir'] = $mibdir; } 1031 if ($error_db['snmp_options'] != $options) { $update_array['snmp_options'] = $options; } 1032 dbUpdate($update_array, 'snmp_errors', '`error_id` = ?', array($error_db['error_id'])); 1033 } else { 1034 dbInsert(array('device_id' => $device['device_id'], 1035 'error_count' => 1, 1036 'error_code' => $error_code, 1037 'error_reason' => $error_codes[$error_code]['reason'], 1038 'snmp_cmd_exitcode' => $GLOBALS['exec_status']['exitcode'], 1039 'snmp_cmd' => $command, 1040 'snmp_options' => $options, 1041 'mib' => $mib, 1042 'mib_dir' => $mibdir, 1043 'oid' => $oid, 1044 'added' => $error_timestamp, 1045 'updated' => $error_timestamp), 'snmp_errors'); 1046 } 1047 } else { 1048 // DEBUG 1049 //logfile('snmp.log', $msg); 1050 } 1051 } 1052 } 1053 1054 $GLOBALS['snmp_error_code'] = $error_code; // Set global variable $snmp_error_code 1055 1056 return $error_code; 1057} 1058 1059/** 1060 * Return SNMP status for last snmp get/walk function 1061 * 1062 * @return boolean SNMP status 1063 */ 1064function snmp_status() 1065{ 1066 return $GLOBALS['snmp_status']; 1067} 1068 1069/** 1070 * Return SNMP error code for last snmp get/walk function 1071 * 1072 * @return integer SNMP error code 1073 */ 1074function snmp_error_code() 1075{ 1076 return $GLOBALS['snmp_error_code']; 1077} 1078 1079/** 1080 * Common SNMP get/walk functions 1081 */ 1082 1083/** 1084 * Uses snmpget to fetch a single OID and returns a string. 1085 * 1086 * @param array $device 1087 * @param string $oid 1088 * @param string $options 1089 * @param string $mib 1090 * @param string $mibdir Optional, correct path should be set in the MIB definition 1091 * @param integer $flags 1092 * @return string 1093 */ 1094function snmp_get($device, $oid, $options = NULL, $mib = NULL, $mibdir = NULL, $flags = OBS_QUOTES_TRIM) 1095{ 1096 1097 if (strpos($oid, ' ')) 1098 { 1099 print_debug("WARNING: snmp_get called for multiple OIDs: $oid"); 1100 } 1101 else if (empty($mib) && str_contains($oid, '::')) 1102 { 1103 // Split Oid names passed as full (ie SNMPv2-MIB::sysUpTime.0) into MIB name (SNMPv2-MIB) and Oid (sysUpTime.0) 1104 list($mib, $oid) = explode('::', $oid); 1105 } 1106 1107 $cmd = snmp_command('snmpget', $device, $oid, $options, $mib, $mibdir, $flags); 1108 1109 $data = external_exec($cmd); 1110 1111 $data = snmp_value_clean($data, $flags); 1112 $GLOBALS['snmp_status'] = ($GLOBALS['exec_status']['exitcode'] === 0 ? TRUE : FALSE); 1113 1114 $GLOBALS['snmp_stats']['snmpget']['count']++; 1115 $GLOBALS['snmp_stats']['snmpget']['time'] += $GLOBALS['exec_status']['runtime']; 1116 1117 if (isset($data[0])) // same as strlen($data) > 0 1118 { 1119 if (preg_match('/(?:No Such Instance|No Such Object|There is no such variable|No more variables left|Authentication failure)/i', $data) || 1120 $data == 'NULL') 1121 { 1122 $data = ''; 1123 $GLOBALS['snmp_status'] = FALSE; 1124 } 1125 } else { 1126 $GLOBALS['snmp_status'] = FALSE; 1127 } 1128 if (OBS_DEBUG) 1129 { 1130 print_message('SNMP STATUS['.($GLOBALS['snmp_status'] ? '%gTRUE': '%rFALSE').'%n]', 'color'); 1131 } 1132 snmp_log_errors('snmpget', $device, $oid, $options, $mib, $mibdir); 1133 1134 return $data; 1135} 1136 1137// DOCME needs phpdoc block 1138// TESTME needs unit testing 1139// FIXME, why strip quotes is default? this removes all quotes also in index 1140function snmp_walk($device, $oid, $options = NULL, $mib = NULL, $mibdir = NULL, $flags = OBS_QUOTES_STRIP) 1141{ 1142 1143 if (empty($mib) && str_contains($oid, '::')) 1144 { 1145 // Split Oid names passed as full (ie SNMPv2-MIB::sysUpTime) into MIB name (SNMPv2-MIB) and Oid (sysUpTime) 1146 list($mib, $oid) = explode('::', $oid); 1147 } 1148 1149 $cmd = snmp_command('snmpwalk', $device, $oid, $options, $mib, $mibdir, $flags); 1150 1151 $data = trim(external_exec($cmd)); 1152 1153 $GLOBALS['snmp_status'] = ($GLOBALS['exec_status']['exitcode'] === 0 ? TRUE : FALSE); 1154 1155 if (is_string($data) && (preg_match("/No Such (Object|Instance)/i", $data))) 1156 { 1157 $data = ''; 1158 $GLOBALS['snmp_status'] = FALSE; 1159 } else { 1160 if (preg_match('/No more variables left in this MIB View \(It is past the end of the MIB tree\)$/', $data) 1161 || preg_match('/End of MIB$/', $data)) 1162 { 1163 # Bit ugly :-( 1164 $d_ex = explode("\n",$data); 1165 $d_ex_count = count($d_ex); 1166 if ($d_ex_count > 1) 1167 { 1168 // Remove last line 1169 unset($d_ex[$d_ex_count-1]); 1170 $data = implode("\n",$d_ex); 1171 } else { 1172 $data = ''; 1173 $GLOBALS['snmp_status'] = FALSE; 1174 } 1175 } 1176 1177 // Concatenate multiline values if not set option -Oq 1178 if (is_flag_set(OBS_SNMP_CONCAT, $flags) && $data && strpos($options, 'q') === FALSE) 1179 { 1180 $old_data = $data; 1181 $data = array(); 1182 foreach (explode("\n", $old_data) as $line) 1183 { 1184 $line = trim($line, " \r"); 1185 if (strpos($line, ' =') !== FALSE) 1186 { 1187 $data[] = $line; 1188 } else { 1189 $key = count($data) - 1; // get previous entry key 1190 list(, $end) = explode(' =', $data[$key], 2); 1191 if ($line !== '' && $end !== '') // add space if previous value not empty 1192 { 1193 $data[$key] .= ' '; 1194 //var_dump($line); 1195 } 1196 //$data[count($data)-1] .= '\n' . $line; // here NOT newline char, but two chars! 1197 $data[$key] .= $line; 1198 } 1199 } 1200 unset($old_data); 1201 $data = implode("\n", $data); 1202 } 1203 } 1204 $GLOBALS['snmp_stats']['snmpwalk']['count']++; 1205 $GLOBALS['snmp_stats']['snmpwalk']['time'] += $GLOBALS['exec_status']['runtime']; 1206 1207 if (OBS_DEBUG) 1208 { 1209 print_message('SNMP STATUS['.($GLOBALS['snmp_status'] ? '%gTRUE': '%rFALSE').'%n]', 'color'); 1210 } 1211 snmp_log_errors('snmpwalk', $device, $oid, $options, $mib, $mibdir); 1212 1213 return $data; 1214} 1215 1216// Cache snmpEngineID 1217// DOCME needs phpdoc block 1218// TESTME needs unit testing 1219function snmp_cache_snmpEngineID($device) 1220{ 1221 global $cache; 1222 1223 if ($device['snmp_version'] == 'v1') { return FALSE; } // snmpEngineID allowed only in v2c/v3 1224 1225 if (!isset($cache['snmp'][$device['device_id']]['snmpEngineID'])) 1226 { 1227 //$snmpEngineID = snmp_get($device, 'snmpEngineID.0', '-OQv', 'SNMP-FRAMEWORK-MIB'); 1228 $snmpEngineID = snmp_get_oid($device, 'snmpEngineID.0', 'SNMP-FRAMEWORK-MIB'); 1229 $snmpEngineID = str_replace(array(' ', '"', "'", "\n", "\r"), '', $snmpEngineID); 1230 1231 if (isset($device['device_id']) && $device['device_id'] > 0) 1232 { 1233 $cache['snmp'][$device['device_id']]['snmpEngineID'] = $snmpEngineID; 1234 } else { 1235 // Correctly use this function, when device_id not set 1236 return $snmpEngineID; 1237 } 1238 } 1239 1240 return $cache['snmp'][$device['device_id']]['snmpEngineID']; 1241} 1242 1243// Cache sysObjectID 1244// DOCME needs phpdoc block 1245// TESTME needs unit testing 1246function snmp_cache_sysObjectID($device) 1247{ 1248 global $cache; 1249 1250 if (!isset($cache['snmp'][$device['device_id']]['sysObjectID'])) 1251 { 1252 //$sysObjectID = snmp_get($device, 'sysObjectID.0', '-Ovqn', 'SNMPv2-MIB'); 1253 $sysObjectID = snmp_get_oid($device, 'sysObjectID.0', 'SNMPv2-MIB', NULL, OBS_SNMP_ALL_NUMERIC); 1254 if (strpos($sysObjectID, 'Wrong Type') !== FALSE) 1255 { 1256 // Wrong Type (should be OBJECT IDENTIFIER): "1.3.6.1.4.1.25651.1.2" 1257 list(, $sysObjectID) = explode(':', $sysObjectID); 1258 $sysObjectID = '.'.trim($sysObjectID, ' ."'); 1259 } 1260 1261 if (isset($device['device_id']) && $device['device_id'] > 0) 1262 { 1263 $cache['snmp'][$device['device_id']]['sysObjectID'] = $sysObjectID; 1264 } else { 1265 // Correctly use this function, when device_id not set 1266 return $sysObjectID; 1267 } 1268 } 1269 1270 return $cache['snmp'][$device['device_id']]['sysObjectID']; 1271} 1272 1273// Return just an array of values without oids. 1274// DOCME needs phpdoc block 1275// TESTME needs unit testing 1276function snmpwalk_values($device, $oid, $array, $mib = NULL, $mibdir = NULL) 1277{ 1278 $options = snmp_gen_options('snmpwalk'); // -OQUs 1279 $data = snmp_walk($device, $oid, $options, $mib, $mibdir); 1280 foreach (explode("\n", $data) as $line) 1281 { 1282 $entry = snmp_parse_line($line); 1283 1284 if (isset($entry['oid_name'][0]) && $entry['index_count'] > 0 && is_valid_snmp_value($entry['value'])) 1285 { 1286 $array[] = $entry['value']; 1287 } 1288 } 1289 1290 return $array; 1291} 1292 1293// Return an array of values with numeric oids as keys 1294// DOCME needs phpdoc block 1295// TESTME needs unit testing 1296function snmpwalk_numericoids($device, $oid, $array, $mib = NULL, $mibdir = NULL) 1297{ 1298 return snmpwalk_cache_oid($device, $oid, $array, $mib, $mibdir, OBS_SNMP_ALL_NUMERIC); 1299} 1300 1301/** 1302 * Uses snmpget to fetch single OID and return string value. 1303 * Differences from snmp_get: 1304 * - not required raw $options, default is -OQv 1305 * 1306 * snmp_get() in-code (r8636) options: 1307 * -Oqv : 252 1308 * -OQv : 149 1309 * -OQUsv : 37 1310 * -OUnqv : 21 1311 * -Onqv : 19 1312 * -Oqsv : 17 1313 * -OQUnv : 14 1314 * -OUqv : 7 1315 * -Onqsv : 3 1316 * -OQUs : 2 1317 * -OUqsv : 1 1318 * -OQnv : 1 1319 * -OQUnsv : 1 1320 * 1321 * snmp_get() cleaned options: 1322 * 'U', 's' has no effect with 'v', 'Q' better than 'q' (no more Wrong Type): 1323 * -OQv : 463 1324 * -OQnv : 59 1325 * -OQUs : 2 1326 * 1327 * snmp_get() each option: 1328 * v : 522 1329 * q : 320 1330 * Q : 204 1331 * U : 83 1332 * s : 61 1333 * n : 59 1334 * 1335 * @param array $device 1336 * @param string $oid 1337 * @param string $mib 1338 * @param string $mibdir Optional, correct path should be set in the MIB definition 1339 * @param integer $flags 1340 * @return string 1341 */ 1342function snmp_get_oid($device, $oid, $mib = NULL, $mibdir = NULL, $flags = OBS_QUOTES_TRIM) 1343{ 1344 1345 $options = snmp_gen_options('snmpget', $flags); 1346 1347 return snmp_get($device, $oid, $options, $mib, $mibdir, $flags); 1348} 1349 1350/** 1351 * Uses snmpgetnext to fetch single OID and return string value. 1352 * 1353 * @param array $device 1354 * @param string $oid 1355 * @param string $mib 1356 * @param string $mibdir Optional, correct path should be set in the MIB definition 1357 * @param integer $flags 1358 * @return string 1359 */ 1360function snmp_getnext_oid($device, $oid, $mib = NULL, $mibdir = NULL, $flags = OBS_QUOTES_TRIM) 1361{ 1362 1363 $options = snmp_gen_options('snmpget', $flags); 1364 1365 if (strpos($oid, ' ')) 1366 { 1367 print_debug("ERROR: snmp_getnext called for multiple OIDs: $oid"); 1368 return NULL; 1369 } 1370 else if (empty($mib) && str_contains($oid, '::')) 1371 { 1372 // Split Oid names passed as full (ie SNMPv2-MIB::sysUpTime.0) into MIB name (SNMPv2-MIB) and Oid (sysUpTime.0) 1373 list($mib, $oid) = explode('::', $oid); 1374 } 1375 1376 $cmd = snmp_command('snmpgetnext', $device, $oid, $options, $mib, $mibdir, $flags); 1377 1378 $data = external_exec($cmd); 1379 1380 $data = snmp_value_clean($data, $flags); 1381 $GLOBALS['snmp_status'] = ($GLOBALS['exec_status']['exitcode'] === 0 ? TRUE : FALSE); 1382 1383 // For counts use just snmpget 1384 $GLOBALS['snmp_stats']['snmpget']['count']++; 1385 $GLOBALS['snmp_stats']['snmpget']['time'] += $GLOBALS['exec_status']['runtime']; 1386 1387 if (isset($data[0])) // same as strlen($data) > 0 1388 { 1389 if (preg_match('/(?:No Such Instance|No Such Object|There is no such variable|No more variables left|Authentication failure)/i', $data) || 1390 $data == 'NULL') 1391 { 1392 $data = ''; 1393 $GLOBALS['snmp_status'] = FALSE; 1394 } 1395 } else { 1396 $GLOBALS['snmp_status'] = FALSE; 1397 } 1398 if (OBS_DEBUG) 1399 { 1400 print_message('SNMP STATUS['.($GLOBALS['snmp_status'] ? '%gTRUE': '%rFALSE').'%n]', 'color'); 1401 } 1402 snmp_log_errors('snmpgetnext', $device, $oid, $options, $mib, $mibdir); 1403 1404 return $data; 1405} 1406 1407/** 1408 * Uses snmpget to fetch multiple OIDs and returns a parsed array. 1409 * Differences from snmp_get_multi: 1410 * - return same array as in snmpwalk_cache_oid() 1411 * - array merges with passed array as in snmpwalk_cache_oid() 1412 * 1413 * @param array $device 1414 * @param array $oids 1415 * @param array $array 1416 * @param string $mib 1417 * @param string $mibdir Optional, correct path should be set in the MIB definition 1418 * @param integer $flags 1419 * @return array 1420 */ 1421// TESTME needs unit testing 1422function snmp_get_multi_oid($device, $oids, $array = array(), $mib = NULL, $mibdir = NULL, $flags = OBS_QUOTES_TRIM) 1423{ 1424 global $config, $cache; 1425 1426 $numeric_oids = is_flag_set(OBS_SNMP_NUMERIC, $flags); // Numeric oids, do not parse oid part 1427 $options = snmp_gen_options('snmpwalk', $flags); // yes, walk 'QUs' 1428 1429 // Oids passed as string and contain multiple Oids? 1430 $oids_multiple = is_string($oids) && strpos($oids, ' ') !== FALSE; 1431 1432 // Detect if snmp max-get param defined for os/model 1433 get_model_array($device); // Pre-cache model options (if required) 1434 1435 // Split Oids list by $max_get count 1436 if (isset($cache['devices']['model'][$device['device_id']]['snmp']['max-get']) && 1437 $cache['devices']['model'][$device['device_id']]['snmp']['max-get'] >= 1) 1438 { 1439 // Device model specific 1440 $max_get = intval($cache['devices']['model'][$device['device_id']]['snmp']['max-get']); 1441 1442 // Convert Oids passed as string to array, for chunk it by defined max-get 1443 if ($oids_multiple) 1444 { 1445 $oids = preg_split('/\s+/', $oids); 1446 } 1447 } 1448 else if (isset($config['os'][$device['os']]['snmp']['max-get']) && 1449 $config['os'][$device['os']]['snmp']['max-get'] >= 1) 1450 { 1451 // OS specific 1452 $max_get = intval($config['os'][$device['os']]['snmp']['max-get']); 1453 1454 // Convert Oids passed as string to array, for chunk it by defined max-get 1455 if ($oids_multiple) 1456 { 1457 $oids = preg_split('/\s+/', $oids); 1458 } 1459 } else { 1460 // Default 1461 $max_get = $config['os_group']['default']['snmp']['max-get']; 1462 //$max_get = 16; 1463 1464 // NOTE. By default, do not convert Oids passed as string to array! 1465 // See notes below 1466 } 1467 1468 if (is_array($oids)) 1469 { 1470 1471 if (OBS_DEBUG && count($oids) > $max_get) 1472 { 1473 print_warning("Passed to snmp_get_multi_oid() Oids count (".count($oids).") more than max-get ($max_get). Command snmpget splitted to multiple chunks."); 1474 } 1475 1476 $data = ''; 1477 $oid_chunks = array_chunk($oids, $max_get); 1478 $GLOBALS['snmp_status'] = FALSE; 1479 foreach ($oid_chunks as $oid_chunk) 1480 { 1481 $oid_text = implode($oid_chunk, ' '); 1482 $cmd = snmp_command('snmpget', $device, $oid_text, $options, $mib, $mibdir, $flags); 1483 $this_data = trim(external_exec($cmd)); 1484 1485 $GLOBALS['snmp_status'] = ($GLOBALS['exec_status']['exitcode'] === 0 ? TRUE : $GLOBALS['snmp_status']); 1486 snmp_log_errors('snmpget', $device, $oid_text, $options, $mib, $mibdir); 1487 $data .= $this_data."\n"; 1488 1489 $GLOBALS['snmp_stats']['snmpget']['count']++; 1490 $GLOBALS['snmp_stats']['snmpget']['time'] += $GLOBALS['exec_status']['runtime']; 1491 } 1492 } else { 1493 // if Oids passed as string, do not split it by chunks, 1494 // ie ports use more than 16 Oids in list, split decrease total polling time 1495 //$oids = explode(' ', trim($oids)); // Convert to array 1496 1497 $cmd = snmp_command('snmpget', $device, $oids, $options, $mib, $mibdir, $flags); 1498 $data = trim(external_exec($cmd)); 1499 1500 $GLOBALS['snmp_status'] = ($GLOBALS['exec_status']['exitcode'] === 0 ? TRUE : FALSE); 1501 snmp_log_errors('snmpget', $device, $oids, $options, $mib, $mibdir); 1502 $GLOBALS['snmp_stats']['snmpget']['count']++; 1503 $GLOBALS['snmp_stats']['snmpget']['time'] += $GLOBALS['exec_status']['runtime']; 1504 } 1505 1506 foreach (explode("\n", $data) as $line) 1507 { 1508 $entry = snmp_parse_line($line, $flags); 1509 1510 // For numeric oids do not split oid and index part 1511 if ($numeric_oids && $entry['index_count'] > 0 && is_valid_snmp_value($entry['value'])) 1512 { 1513 $array[$entry['index']] = $entry['value']; 1514 continue; 1515 } 1516 1517 //list($oid, $index) = explode('.', $oid, 2); 1518 if (isset($entry['oid_name'][0]) && $entry['index_count'] > 0 && is_valid_snmp_value($entry['value'])) 1519 { 1520 $array[$entry['index']][$entry['oid_name']] = $entry['value']; 1521 } 1522 } 1523 1524 if (empty($array)) 1525 { 1526 $GLOBALS['snmp_status'] = FALSE; 1527 snmp_log_errors('snmpget', $device, $oids, $options, $mib, $mibdir); 1528 } 1529 1530 if (OBS_DEBUG) 1531 { 1532 print_message('SNMP STATUS['.($GLOBALS['snmp_status'] ? '%gTRUE': '%rFALSE').'%n]', 'color'); 1533 } 1534 1535 return $array; 1536} 1537 1538/** 1539 * Uses snmpwalk to fetch a single OID and returns a array. 1540 * 1541 * @param array $device 1542 * @param string $oid 1543 * @param array $array 1544 * @param string $mib 1545 * @param string $mibdir Optional, correct path should be set in the MIB definition 1546 * @param integer $flags 1547 * @return array 1548 */ 1549function snmpwalk_cache_oid($device, $oid, $array, $mib = NULL, $mibdir = NULL, $flags = OBS_SNMP_ALL) 1550{ 1551 $numeric_oids = is_flag_set(OBS_SNMP_NUMERIC, $flags); // Numeric oids, do not parse oid part 1552 $options = snmp_gen_options('snmpwalk', $flags); 1553 1554 $data = snmp_walk($device, $oid, $options, $mib, $mibdir, $flags); 1555 foreach (explode("\n", $data) as $line) 1556 { 1557 $entry = snmp_parse_line($line, $flags); 1558 1559 // For numeric oids do not split oid and index part 1560 if ($numeric_oids && $entry['index_count'] > 0 && is_valid_snmp_value($entry['value'])) 1561 { 1562 $array[$entry['index']] = $entry['value']; 1563 continue; 1564 } 1565 1566 if (isset($entry['oid_name'][0]) && $entry['index_count'] > 0 && is_valid_snmp_value($entry['value'])) 1567 { 1568 $array[$entry['index']][$entry['oid_name']] = $entry['value']; 1569 } 1570 } 1571 1572 return $array; 1573 1574} 1575 1576// just like snmpwalk_cache_oid except that it returns the numerical oid as the index 1577// this is useful when the oid is indexed by the mac address and snmpwalk would 1578// return periods (.) for non-printable numbers, thus making many different indexes appear 1579// to be the same. 1580// DOCME needs phpdoc block 1581// TESTME needs unit testing 1582// CLEANME (deprecated) not used anymore 1583function snmpwalk_cache_oid_num($device, $oid, $array, $mib = NULL, $mibdir = NULL) 1584{ 1585 return snmpwalk_cache_oid($device, $oid, $array, $mib, $mibdir, OBS_SNMP_ALL_NUMERIC); 1586} 1587 1588// just like snmpwalk_cache_oid_num (it returns the numerical oid as the index), 1589// but use snmptranslate for cut mib part from index 1590// DOCME needs phpdoc block 1591// TESTME needs unit testing 1592// FIXME. maybe override function snmpwalk_cache_oid_num()? 1593function snmpwalk_cache_oid_num2($device, $oid, $array, $mib = NULL, $mibdir = NULL, $flags = OBS_SNMP_ALL_NUMERIC) 1594{ 1595 $options = snmp_gen_options('snmpwalk', $flags | OBS_SNMP_NUMERIC); // This function always use OBS_SNMP_NUMERIC 1596 1597 $oid_num = snmp_translate($oid, $mib, $mibdir); 1598 //$data = snmp_walk($device, $oid, $options, $mib, $mibdir, $flags); 1599 $data = snmp_walk($device, $oid_num, $options, $mib, $mibdir, $flags); 1600 1601 $pattern = '/^' . str_replace('.', '\.', $oid_num) . '\./'; 1602 1603 foreach (explode("\n", $data) as $entry) 1604 { 1605 list($oid_num, $value) = explode('=', $entry, 2); 1606 $oid_num = trim($oid_num); 1607 $value = snmp_value_clean($value, $flags); 1608 $index = preg_replace($pattern, '', $oid_num); 1609 1610 if (isset($oid) && isset($index[0]) && is_valid_snmp_value($value)) 1611 { 1612 $array[$index][$oid] = $value; 1613 } 1614 } 1615 1616 return $array; 1617} 1618 1619// DOCME needs phpdoc block 1620// TESTME needs unit testing 1621// used only in discovery/processors/juniper-system-mib.inc.php 1622function snmpwalk_cache_bare_oid($device, $oid, $array, $mib = NULL, $mibdir = NULL, $flags = OBS_SNMP_ALL) 1623{ 1624 // Always use snmpwalk_cache_oid() for numeric 1625 if (is_flag_set(OBS_SNMP_NUMERIC, $flags)) 1626 { 1627 return snmpwalk_cache_oid($device, $oid, $array, $mib, $mibdir, $flags); 1628 } 1629 1630 $options = snmp_gen_options('snmpwalk', $flags); 1631 1632 $data = snmp_walk($device, $oid, $options, $mib, $mibdir, $flags); 1633 foreach (explode("\n", $data) as $line) 1634 { 1635 $entry = snmp_parse_line($line, $flags); 1636 1637 // Not know why, but here removed index part more than 2, here old code: 1638 // list($r_oid, $first, $second) = explode('.', $r_oid); 1639 if (isset($entry['oid']) && is_valid_snmp_value($entry['value'])) 1640 { 1641 $array[$entry['oid']] = $entry['value']; 1642 } 1643 } 1644 1645 return $array; 1646} 1647 1648 1649// DOCME needs phpdoc block 1650// TESTME needs unit testing 1651// used only in discovery/processors/juniper-system-mib.inc.php 1652function snmpwalk_cache_double_oid($device, $oid, $array, $mib = NULL, $mibdir = NULL, $flags = OBS_SNMP_ALL) 1653{ 1654 // Always use snmpwalk_cache_oid() for numeric 1655 if (is_flag_set(OBS_SNMP_NUMERIC, $flags)) 1656 { 1657 return snmpwalk_cache_oid($device, $oid, $array, $mib, $mibdir, $flags); 1658 } 1659 1660 $options = snmp_gen_options('snmpwalk', $flags); 1661 1662 $index_count = 2; 1663 $data = snmp_walk($device, $oid, $options, $mib, $mibdir, $flags); 1664 foreach (explode("\n", $data) as $line) 1665 { 1666 $entry = snmp_parse_line($line, $flags); 1667 1668 // Not know why, but here removed index part more than 2, here old code: 1669 // list($r_oid, $first, $second) = explode('.', $r_oid); 1670 if (isset($entry['oid_name'][0]) && $entry['index_count'] >= $index_count && is_valid_snmp_value($entry['value'])) 1671 { 1672 $index = implode('.', array_slice($entry['index_parts'], 0, $index_count)); 1673 $array[$index][$entry['oid_name']] = $entry['value']; 1674 } 1675 } 1676 1677 return $array; 1678} 1679 1680// DOCME needs phpdoc block 1681// TESTME needs unit testing 1682function snmpwalk_cache_triple_oid($device, $oid, $array, $mib = NULL, $mibdir = NULL, $flags = OBS_SNMP_ALL) 1683{ 1684 // Always use snmpwalk_cache_oid() for numeric 1685 if (is_flag_set(OBS_SNMP_NUMERIC, $flags)) 1686 { 1687 return snmpwalk_cache_oid($device, $oid, $array, $mib, $mibdir, $flags); 1688 } 1689 1690 $options = snmp_gen_options('snmpwalk', $flags); 1691 1692 $index_count = 3; // Not know why, but here removed index part more than 3 1693 $data = snmp_walk($device, $oid, $options, $mib, $mibdir, $flags); 1694 foreach (explode("\n", $data) as $line) 1695 { 1696 $entry = snmp_parse_line($line, $flags); 1697 1698 // Not know why, but here removed index part more than 3, here old code: 1699 // list($r_oid, $first, $second, $tried) = explode('.', $r_oid); 1700 if (isset($entry['oid_name'][0]) && $entry['index_count'] >= $index_count && is_valid_snmp_value($entry['value'])) 1701 { 1702 $index = implode('.', array_slice($entry['index_parts'], 0, $index_count)); 1703 $array[$index][$entry['oid_name']] = $entry['value']; 1704 } 1705 } 1706 1707 return $array; 1708} 1709 1710// DOCME needs phpdoc block 1711// TESTME needs unit testing 1712function snmpwalk_cache_twopart_oid($device, $oid, $array, $mib = NULL, $mibdir = NULL, $flags = OBS_SNMP_ALL) 1713{ 1714 // Always use snmpwalk_cache_oid() for numeric 1715 if (is_flag_set(OBS_SNMP_NUMERIC, $flags)) 1716 { 1717 return snmpwalk_cache_oid($device, $oid, $array, $mib, $mibdir, $flags); 1718 } 1719 1720 $options = snmp_gen_options('snmpwalk', $flags); 1721 1722 $index_count = 2; 1723 $data = snmp_walk($device, $oid, $options, $mib, $mibdir, $flags); 1724 foreach (explode("\n", $data) as $line) 1725 { 1726 $entry = snmp_parse_line($line, $flags); 1727 1728 if (isset($entry['oid_name'][0]) && $entry['index_count'] >= $index_count && is_valid_snmp_value($entry['value'])) 1729 { 1730 $first = array_shift($entry['index_parts']); 1731 $second = implode('.', $entry['index_parts']); 1732 1733 $array[$first][$second][$entry['oid_name']] = $entry['value']; 1734 } 1735 } 1736 1737 return $array; 1738} 1739 1740// DOCME needs phpdoc block 1741// TESTME needs unit testing 1742function snmpwalk_cache_threepart_oid($device, $oid, $array, $mib = NULL, $mibdir = NULL, $flags = OBS_SNMP_ALL) 1743{ 1744 // Always use snmpwalk_cache_oid() for numeric 1745 if (is_flag_set(OBS_SNMP_NUMERIC, $flags)) 1746 { 1747 return snmpwalk_cache_oid($device, $oid, $array, $mib, $mibdir, $flags); 1748 } 1749 1750 $options = snmp_gen_options('snmpwalk', $flags); 1751 1752 $index_count = 3; 1753 $data = snmp_walk($device, $oid, $options, $mib, $mibdir, $flags); 1754 1755 foreach (explode("\n", $data) as $line) 1756 { 1757 $entry = snmp_parse_line($line, $flags); 1758 if (isset($entry['oid_name'][0]) && $entry['index_count'] >= $index_count && is_valid_snmp_value($entry['value'])) 1759 { 1760 $first = array_shift($entry['index_parts']); 1761 $second = array_shift($entry['index_parts']); 1762 $third = implode('.', $entry['index_parts']); 1763 $array[$first][$second][$third][$entry['oid_name']] = $entry['value']; 1764 } 1765 } 1766 1767 return $array; 1768} 1769 1770/** 1771 * SNMP walk and parse tables with any (not limited) count of index parts into multilevel array. 1772 * Array levels same as count index parts. Ie: someOid.1.2.3.4 -> 4 index parts, and result array also will have 4 levels 1773 * 1774 * @param array $device Device array 1775 * @param string $oid Table OID name 1776 * @param array $array Array from previous snmpwalk for merge (or empty) 1777 * @param string $mib MIB name 1778 * @param mixed $mibdir Array or string with MIB dirs list, by default used dir from MIB definitions 1779 * @param integer $flags SNMP walk/parse flags 1780 * 1781 * @return array Prsed array with content from requested Table 1782 */ 1783function snmp_walk_multipart_oid($device, $oid, $array, $mib = NULL, $mibdir = NULL, $flags = OBS_QUOTES_TRIM) 1784{ 1785 // Always use snmpwalk_cache_oid() for numeric 1786 if (is_flag_set(OBS_SNMP_NUMERIC, $flags)) 1787 { 1788 return snmpwalk_cache_oid($device, $oid, $array, $mib, $mibdir, $flags); 1789 } 1790 1791 $options = snmp_gen_options('snmpwalk', $flags); 1792 1793 $index_count = 1; 1794 $data = snmp_walk($device, $oid, $options, $mib, $mibdir, $flags); 1795 foreach (explode("\n", $data) as $line) 1796 { 1797 $entry = snmp_parse_line($line, $flags); 1798 //print_vars($entry); 1799 1800 if (isset($entry['oid_name'][0]) && $entry['index_count'] >= $index_count && is_valid_snmp_value($entry['value'])) 1801 { 1802 $entry_array = array($entry['oid_name'] => $entry['value']); 1803 for ($i = $entry['index_count'] - 1; $i >= 0; $i--) 1804 { 1805 $entry_array = array($entry['index_parts'][$i] => $entry_array); 1806 } 1807 $array = array_replace_recursive((array)$array, $entry_array); 1808 1809 /* this seems retarded. need a way to just build this automatically. 1810 switch ($entry['index_count']) 1811 { 1812 case 1: 1813 $array[$entry['index_parts'][0]][$entry['oid_name']] = $entry['value']; 1814 break; 1815 case 2: 1816 $array[$entry['index_parts'][0]][$entry['index_parts'][1]][$entry['oid_name']] = $entry['value']; 1817 break; 1818 case 3: 1819 $array[$entry['index_parts'][0]][$entry['index_parts'][1]][$entry['index_parts'][2]][$entry['oid_name']] = $entry['value']; 1820 break; 1821 case 4: 1822 $array[$entry['index_parts'][0]][$entry['index_parts'][1]][$entry['index_parts'][2]][$entry['index_parts'][3]][$entry['oid_name']] = $entry['value']; 1823 break; 1824 case 5: 1825 $array[$entry['index_parts'][0]][$entry['index_parts'][1]][$entry['index_parts'][2]][$entry['index_parts'][3]][$entry['index_parts'][4]][$entry['oid_name']] = $entry['value']; 1826 break; 1827 } 1828 */ 1829 } 1830 } 1831 1832 return $array; 1833} 1834 1835/** 1836 * Initialize (start) snmpsimd daemon, for tests or other purposes. 1837 * Stop daemon not required, because here registered shutdown_function for kill daemon at end of run script(s) 1838 * 1839 * @param string $snmpsimd_data Data DIR, where *.snmprec placed 1840 * @param string $snmpsimd_ip Local IP which used for daemon (default 127.0.0.1) 1841 * @param integer $snmpsimd_port Local Port which used for daemon (default 16111) 1842 */ 1843function snmpsimd_init($snmpsimd_data, $snmpsimd_ip = '127.0.0.1', $snmpsimd_port = 16111) 1844{ 1845 global $config; 1846 1847 if (str_contains($snmpsimd_ip, ':')) 1848 { 1849 // IPv6 1850 $ifconfig_cmd = "ifconfig | grep 'inet6 addr:$snmpsimd_ip' | cut -d: -f2 | awk '{print $1}'"; 1851 $snmpsimd_end = 'udpv6'; 1852 } else { 1853 $ifconfig_cmd = "ifconfig | grep 'inet addr:$snmpsimd_ip' | cut -d: -f2 | awk '{print $1}'"; 1854 $snmpsimd_end = 'udpv4'; 1855 } 1856 $snmpsimd_ip = external_exec($ifconfig_cmd); 1857 1858 if ($snmpsimd_ip) 1859 { 1860 //$snmpsimd_port = 16111; 1861 1862 // Detect snmpsimd command path 1863 $snmpsimd_path = external_exec('which snmpsimd.py'); 1864 if (empty($snmpsimd_path)) 1865 { 1866 foreach (array('/usr/local/bin/', '/usr/bin/', '/usr/sbin/') as $path) 1867 { 1868 if (is_executable($path . 'snmpsimd.py')) 1869 { 1870 $snmpsimd_path = $path . 'snmpsimd.py'; 1871 break; 1872 } 1873 else if (is_executable($path . 'snmpsimd')) 1874 { 1875 $snmpsimd_path = $path . 'snmpsimd'; 1876 break; 1877 } 1878 } 1879 } 1880 //var_dump($snmpsimd_path); 1881 1882 if (empty($snmpsimd_path)) 1883 { 1884 print_warning("snmpsimd not found, please install it first."); 1885 } else { 1886 //$snmpsimd_data = dirname(__FILE__) . '/data/os'; 1887 1888 $tmp_path = empty($config['temp_dir']) ? '/tmp' : $config['temp_dir']; // GLOBALS empty in php units 1889 1890 $snmpsimd_pid = $tmp_path.'/observium_snmpsimd.pid'; 1891 $snmpsimd_log = $tmp_path.'/observium_snmpsimd.log'; 1892 1893 if (is_file($snmpsimd_pid)) 1894 { 1895 // Kill stale snmpsimd process 1896 $pid = file_get_contents($snmpsimd_pid); 1897 $info = get_pid_info($pid); 1898 //var_dump($info); 1899 if (str_contains($info['COMMAND'], 'snmpsimd')) 1900 { 1901 external_exec("kill -9 $pid"); 1902 } 1903 unlink($snmpsimd_pid); 1904 } 1905 1906 $snmpsimd_cmd = "$snmpsimd_path --daemonize --data-dir=$snmpsimd_data --agent-$snmpsimd_end-endpoint=$snmpsimd_ip:$snmpsimd_port --pid-file=$snmpsimd_pid --logging-method=file:$snmpsimd_log"; 1907 //var_dump($snmpsimd_cmd); 1908 1909 external_exec($snmpsimd_cmd); 1910 $pid = file_get_contents($snmpsimd_pid); 1911 if ($pid) 1912 { 1913 define('OBS_SNMPSIMD', TRUE); 1914 register_shutdown_function(function($snmpsimd_pid){ 1915 $pid = file_get_contents($snmpsimd_pid); 1916 //echo "KILL'em all! PID: $pid\n"; 1917 external_exec("kill -9 $pid"); 1918 unlink($snmpsimd_pid); 1919 }, $snmpsimd_pid); 1920 } 1921 } 1922 //exit; 1923 } else { 1924 print_warning("Local IP $snmpsimd_ip unavailable. SNMP simulator not started."); 1925 } 1926 if (!defined('OBS_SNMPSIMD')) 1927 { 1928 define('OBS_SNMPSIMD', FALSE); 1929 } 1930} 1931 1932/** 1933 * Take -OXqs output and parse it into an array containing OID array and the value 1934 * Hopefully this is the beginning of more intelligent OID parsing! 1935 * Thanks to David Farrell <DavidPFarrell@gmail.com> for the parser solution. 1936 * This function is free for use by all with attribution to David. 1937 * 1938 * @return array 1939 * @param $string 1940 */ 1941// TESTME needs unit testing 1942function parse_oid2($string) 1943{ 1944 $result = array(); 1945 $matches = array(); 1946 1947 // Match OID - If wrapped in double-quotes ('"'), must escape '"', else must escape ' ' (space) or '[' - Other escaping is optional 1948 $match_count = preg_match('/^(?:((?!")(?:[^\\\\\\[ ]|(?:\\\\.))+)|(?:"((?:[^\\\\\"]|(?:\\\\.))+)"))/', $string, $matches); 1949 if (null !== $match_count && $match_count > 0) 1950 { 1951 // [1] = unquoted, [2] = quoted 1952 $value = strlen($matches[1]) > 0 ? $matches[1] : $matches[2]; 1953 $result[] = stripslashes($value); 1954 1955 // I do this (vs keeping track of offset) to use ^ in regex 1956 $string = substr($string, strlen($matches[0])); 1957 1958 // Match indexes (optional) - If wrapped in double-quotes ('"'), must escape '"', else must escape ']' - Other escaping is optional 1959 while (true) 1960 { 1961 $match_count = preg_match('/^\\[(?:((?!")(?:[^\\\\\\]]|(?:\\\\.))+)|(?:"((?:[^\\\\\"]|(?:\\\\.))+)"))\\]/', $string, $matches); 1962 if (null !== $match_count && $match_count > 0) 1963 { 1964 // [1] = unquoted, [2] = quoted 1965 $value = strlen($matches[1]) > 0 ? $matches[1] : $matches[2]; 1966 $result[] = stripslashes($value); 1967 1968 // I do this (vs keeping track of offset) to use ^ in regex 1969 $string = substr($string, strlen($matches[0])); 1970 } 1971 else 1972 { 1973 break; 1974 } 1975 } // while 1976 1977 // Match value - Skips leading ' ' characters - If remainder is wrapped in double-quotes ('"'), must escape '"', other escaping is optional 1978 $match_count = preg_match('/^\\s+(?:((?!")(?:[^\\\\]|(?:\\\\.))+)|(?:"((?:[^\\\\\"]|(?:\\\\.))+)"))$/', $string, $matches); 1979 if (null !== $match_count && $match_count > 0) 1980 { 1981 // [1] = unquoted, [2] = quoted 1982 $value = strlen($matches[1]) > 0 ? $matches[1] : $matches[2]; 1983 1984 $result[] = stripslashes($value); 1985 1986 if (strlen($string) != strlen($matches[0])) { echo 'Length error!'; return null; } 1987 1988 return $result; 1989 } 1990 } 1991 1992 // All or nothing 1993 return null; 1994} 1995 1996/** 1997 * Take -Oqs output and parse it into an array containing OID array and the value 1998 * Hopefully this is the beginning of more intelligent OID parsing! 1999 * Thanks to David Farrell <DavidPFarrell@gmail.com> for the parser solution. 2000 * This function is free for use by all with attribution to David. 2001 * 2002 * @return array 2003 * @param $string 2004 */ 2005// TESTME needs unit testing 2006// CLEANME (deprecated) not used anymore 2007function parse_oid($string) 2008{ 2009 $result = array(); 2010 while (true) 2011 { 2012 $matches = array(); 2013 $match_count = preg_match('/^(?:((?:[^\\\\\\. "]|(?:\\\\.))+)|(?:"((?:[^\\\\"]|(?:\\\\.))+)"))((?:[\\. ])|$)/', $string, $matches); 2014 if (null !== $match_count && $match_count > 0) 2015 { 2016 // [1] = unquoted, [2] = quoted 2017 $value = strlen($matches[1]) > 0 ? $matches[1] : $matches[2]; 2018 $result[] = stripslashes($value); 2019 2020 // Are we expecting any more parts? 2021 if (strlen($matches[3]) > 0) 2022 { 2023 // I do this (vs keeping track of offset) to use ^ in regex 2024 $string = substr($string, strlen($matches[0])); 2025 } 2026 else 2027 { 2028 $ret['value'] = array_pop($result); 2029 $ret['oid'] = $result; 2030 return $ret; 2031 } 2032 } 2033 else 2034 { 2035 // All or nothing 2036 return null; 2037 } 2038 } // while 2039} 2040 2041// DOCME needs phpdoc block 2042// TESTME needs unit testing 2043// CLEANME (deprecated) not used anymore 2044function snmp_walk_parser2($device, $oid, $oid_elements, $mib = NULL, $mibdir = NULL) 2045{ 2046 $data = snmp_walk($device, $oid, '-Oqs', $mib, $mibdir, FALSE); 2047 foreach (explode("\n", $data) as $text) 2048 { 2049 $ret = parse_oid2($text); 2050 if (!empty($ret['value'])) 2051 { 2052 // this seems retarded. need a way to just build this automatically. 2053 switch ($oid_elements) 2054 { 2055 case 1: 2056 $array[$ret['oid'][0]] = $ret['value']; 2057 break; 2058 case 2: 2059 $array[$ret['oid'][1]][$ret['oid'][0]] = $ret['value']; 2060 break; 2061 case 3: 2062 $array[$ret['oid'][1]][$ret['oid'][2]][$ret['oid'][0]] = $ret['value']; 2063 break; 2064 case 4: 2065 $array[$ret['oid'][1]][$ret['oid'][2]][$ret['oid'][3]][$ret['oid'][0]] = $ret['value']; 2066 break; 2067 } 2068 } 2069 } 2070 return $array; 2071} 2072 2073// DOCME needs phpdoc block 2074// TESTME needs unit testing 2075function snmp_walk_parser($device, $oid, $oid_elements, $mib = NULL, $mibdir = NULL) 2076{ 2077 $data = snmp_walk($device, $oid, '-Oqs', $mib, $mibdir, FALSE); 2078 foreach (explode("\n", $data) as $text) 2079 { 2080 $ret = parse_oid($text); 2081 if (!empty($ret['value'])) 2082 { 2083 // this seems retarded. need a way to just build this automatically. 2084 switch ($oid_elements) 2085 { 2086 case 1: 2087 $array[$ret['oid'][0]] = $ret['value']; 2088 break; 2089 case 2: 2090 $array[$ret['oid'][1]][$ret['oid'][0]] = $ret['value']; 2091 break; 2092 case 3: 2093 $array[$ret['oid'][1]][$ret['oid'][2]][$ret['oid'][0]] = $ret['value']; 2094 break; 2095 case 4: 2096 $array[$ret['oid'][1]][$ret['oid'][2]][$ret['oid'][3]][$ret['oid'][0]] = $ret['value']; 2097 break; 2098 } 2099 } 2100 } 2101 2102 return $array; 2103} 2104 2105 2106// CLEANME (deprecated) duplicate for snmpwalk_cache_oid 2107function snmpwalk_cache_multi_oid($device, $oid, $array, $mib = NULL, $mibdir = NULL, $flags = OBS_SNMP_ALL) 2108{ 2109 return snmpwalk_cache_oid($device, $oid, $array, $mib, $mibdir, $flags); 2110} 2111 2112// CLEANME (deprecated) not used anymore 2113function snmp_parser_quote($m) 2114{ 2115 return str_replace(array('.',' '), 2116 array('PLACEHOLDER-DOT', 'PLACEHOLDER-SPACE'), $m[1]); 2117} 2118 2119// CLEANME (deprecated) not used anymore 2120function snmp_parser_unquote($str) 2121{ 2122 return str_replace(array('PLACEHOLDER-DOT', 'PLACEHOLDER-SPACE', 'PLACEHOLDER-ESCAPED-QUOTE'), 2123 array('.',' ','"'), $str); 2124} 2125 2126// CLEANME (deprecated) not used anymore 2127function ascii_to_oid($string) 2128{ 2129 return snmp_string_to_oid($string); 2130} 2131 2132// CLEANME (deprecated) not used anymore 2133function string_to_oid($string) 2134{ 2135 return snmp_string_to_oid($string); 2136} 2137 2138// Return table from array if already walked, else walk it. 2139// Currently overwrites arrays passed as $array, array_merge_indexed didn't like non-numeric indexes? 2140function snmp_cache_table($device, $table, $array, $mib, $mibdir = NULL, $flags = NULL) 2141{ 2142 2143 // We seem to have been passed a MIB::oidName format. Split it. 2144 if (strpos($table, '::')) { list($mib, $table) = explode("::", $table); } 2145 2146 if (isset($GLOBALS['cache_snmp'][$device['device_id']][$mib][$table]) && is_array($GLOBALS['cache_snmp'][$device['device_id']][$mib][$table])) 2147 { 2148 print_debug("Get cached Table OID: $mib::$table"); 2149 $array = array_merge_indexed($GLOBALS['cache_snmp'][$device['device_id']][$mib][$table], $array); 2150 //$array = $GLOBALS['cache_snmp'][$device['device_id']][$mib][$table]; 2151 } else { 2152 $walk = snmpwalk_cache_oid($device, $table, array(), $mib, $mibdir, $flags); 2153 if (!isset($GLOBALS['cache_snmp'][$device['device_id']][$mib][$table]) && $walk) 2154 { 2155 print_debug("Store in cache Table OID: $mib::$table"); 2156 $GLOBALS['cache_snmp'][$device['device_id']][$mib][$table] = $walk; 2157 $array = array_merge_indexed($walk, $array); 2158 } 2159 //$array = $walk; 2160 } 2161 return $array; 2162} 2163 2164// Return oid from cache if already fetched, else fetch it. 2165// Currently overwrites arrays passed as $array, array_merge_indexed didn't like non-numeric indexes? 2166// FIXME -- handle multiple OIDs (as individual cache entries) 2167function snmp_cache_oid($device, $oid, $mib = NULL, $mibdir = NULL, $flags = NULL) 2168{ 2169 2170 // We seem to have been passed a MIB::oidName format. Split it. 2171 if (strpos($oid, '::')) { list($mib, $oid) = explode("::", $oid); } 2172 2173 if (isset($GLOBALS['cache_snmp'][$device['device_id']][$mib][$oid])) 2174 { 2175 print_debug("Get cached OID: $mib::$oid"); 2176 $value = $GLOBALS['cache_snmp'][$device['device_id']][$mib][$oid]; 2177 } else { 2178 $value = snmp_get_oid($device, $oid, $mib, $mibdir, $flags); 2179 2180 if (!isset($cache_snmp[$mib][$oid])) 2181 { 2182 print_debug("Store in cache OID: $mib::$oid"); 2183 $GLOBALS['cache_snmp'][$device['device_id']][$mib][$oid] = $value; 2184 } 2185 2186 } 2187 2188 return $value; 2189} 2190 2191// EOF 2192