Issues (2963)

includes/discovery/functions.inc.php (2 issues)

1
<?php
2
3
/*
4
 * LibreNMS Network Management and Monitoring System
5
 * Copyright (C) 2006-2011, Observium Developers - http://www.observium.org
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * See COPYING for more details.
13
 */
14
15
use Illuminate\Support\Str;
16
use LibreNMS\Config;
17
use LibreNMS\Device\YamlDiscovery;
18
use LibreNMS\Exceptions\HostExistsException;
19
use LibreNMS\Exceptions\InvalidIpException;
20
use LibreNMS\Util\IP;
21
use LibreNMS\Util\IPv6;
22
23
function discover_new_device($hostname, $device = [], $method = '', $interface = '')
24
{
25
    d_echo("discovering $hostname\n");
26
27
    if (IP::isValid($hostname)) {
28
        $ip = $hostname;
29
        if (! Config::get('discovery_by_ip', false)) {
30
            d_echo('Discovery by IP disabled, skipping ' . $hostname);
31
            log_event("$method discovery of " . $hostname . ' failed - Discovery by IP disabled', $device['device_id'], 'discovery', 4);
32
33
            return false;
34
        }
35
    } elseif (\LibreNMS\Util\Validate::hostname($hostname)) {
36
        if ($mydomain = Config::get('mydomain')) {
37
            $full_host = rtrim($hostname, '.') . '.' . $mydomain;
38
            if (isDomainResolves($full_host)) {
39
                $hostname = $full_host;
40
            }
41
        }
42
43
        $ip = gethostbyname($hostname);
44
        if ($ip == $hostname) {
45
            d_echo("name lookup of $hostname failed\n");
46
            log_event("$method discovery of " . $hostname . ' failed - Check name lookup', $device['device_id'], 'discovery', 5);
47
48
            return false;
49
        }
50
    } else {
51
        d_echo("Discovery failed: '$hostname' is not a valid ip or dns name\n");
52
53
        return false;
54
    }
55
56
    d_echo("ip lookup result: $ip\n");
57
58
    $hostname = rtrim($hostname, '.'); // remove trailing dot
59
60
    $ip = IP::parse($ip, true);
61
    if ($ip->inNetworks(Config::get('autodiscovery.nets-exclude'))) {
62
        d_echo("$ip in an excluded network - skipping\n");
63
64
        return false;
65
    }
66
67
    if (! $ip->inNetworks(Config::get('nets'))) {
68
        d_echo("$ip not in a matched network - skipping\n");
69
70
        return false;
71
    }
72
73
    try {
74
        $remote_device_id = addHost($hostname, '', '161', 'udp', Config::get('default_poller_group'));
75
        $remote_device = device_by_id_cache($remote_device_id, 1);
76
        echo '+[' . $remote_device['hostname'] . '(' . $remote_device['device_id'] . ')]';
77
        discover_device($remote_device);
78
        device_by_id_cache($remote_device_id, 1);
79
80
        if ($remote_device_id && is_array($device) && ! empty($method)) {
81
            $extra_log = '';
82
            $int = cleanPort($interface);
83
            if (is_array($int)) {
84
                $extra_log = ' (port ' . $int['label'] . ') ';
85
            }
86
87
            log_event('Device ' . $remote_device['hostname'] . " ($ip) $extra_log autodiscovered through $method on " . $device['hostname'], $remote_device_id, 'discovery', 1);
88
        } else {
89
            log_event("$method discovery of " . $remote_device['hostname'] . " ($ip) failed - Check ping and SNMP access", $device['device_id'], 'discovery', 5);
90
        }
91
92
        return $remote_device_id;
93
    } catch (HostExistsException $e) {
94
        // already have this device
95
    } catch (Exception $e) {
96
        log_event("$method discovery of " . $hostname . " ($ip) failed - " . $e->getMessage(), $device['device_id'], 'discovery', 5);
97
    }
98
99
    return false;
100
}
101
//end discover_new_device()
102
103
/**
104
 * @param $device
105
 */
106
function load_discovery(&$device)
107
{
108
    $yaml_discovery = Config::get('install_dir') . '/includes/definitions/discovery/' . $device['os'] . '.yaml';
109
    if (file_exists($yaml_discovery)) {
110
        $device['dynamic_discovery'] = Symfony\Component\Yaml\Yaml::parse(
111
            file_get_contents($yaml_discovery)
112
        );
113
    } else {
114
        unset($device['dynamic_discovery']);
115
    }
116
}
117
118
/**
119
 * @param  array  $device  The device to poll
120
 * @param  bool  $force_module  Ignore device module overrides
121
 * @return bool if the device was discovered or skipped
122
 */
123
function discover_device(&$device, $force_module = false)
124
{
125
    if ($device['snmp_disable'] == '1') {
126
        return true;
127
    }
128
129
    global $valid;
130
131
    $valid = [];
132
    // Reset $valid array
133
    $attribs = DeviceCache::getPrimary()->getAttribs();
134
    $device['attribs'] = $attribs;
135
136
    $device_start = microtime(true);
137
    // Start counting device poll time
138
    echo $device['hostname'] . ' ' . $device['device_id'] . ' ' . $device['os'] . ' ';
139
140
    $response = device_is_up($device, true);
141
142
    if ($response['status'] !== '1') {
143
        return false;
144
    }
145
146
    $discovery_devices = Config::get('discovery_modules', []);
147
    $discovery_devices = ['core' => true] + $discovery_devices;
148
149
    foreach ($discovery_devices as $module => $module_status) {
150
        $os_module_status = Config::getOsSetting($device['os'], "discovery_modules.$module");
151
        d_echo('Modules status: Global' . (isset($module_status) ? ($module_status ? '+ ' : '- ') : '  '));
152
        d_echo('OS' . (isset($os_module_status) ? ($os_module_status ? '+ ' : '- ') : '  '));
153
        d_echo('Device' . (isset($attribs['discover_' . $module]) ? ($attribs['discover_' . $module] ? '+ ' : '- ') : '  '));
154
        if ($force_module === true ||
155
            ! empty($attribs['discover_' . $module]) ||
156
            ($os_module_status && ! isset($attribs['discover_' . $module])) ||
157
            ($module_status && ! isset($os_module_status) && ! isset($attribs['discover_' . $module]))
158
        ) {
159
            $module_start = microtime(true);
160
            $start_memory = memory_get_usage();
161
            echo "\n#### Load disco module $module ####\n";
162
163
            try {
164
                include "includes/discovery/$module.inc.php";
165
            } catch (Exception $e) {
166
                // isolate module exceptions so they don't disrupt the polling process
167
                echo $e->getTraceAsString() . PHP_EOL;
168
                c_echo("%rError in $module module.%n " . $e->getMessage() . PHP_EOL);
169
                logfile("Error in $module module. " . $e->getMessage() . PHP_EOL . $e->getTraceAsString() . PHP_EOL);
170
            }
171
172
            $module_time = microtime(true) - $module_start;
173
            $module_time = substr($module_time, 0, 5);
174
            $module_mem = (memory_get_usage() - $start_memory);
175
            printf("\n>> Runtime for discovery module '%s': %.4f seconds with %s bytes\n", $module, $module_time, $module_mem);
176
            printChangedStats();
177
            echo "#### Unload disco module $module ####\n\n";
178
        } elseif (isset($attribs['discover_' . $module]) && $attribs['discover_' . $module] == '0') {
179
            echo "Module [ $module ] disabled on host.\n\n";
180
        } elseif (isset($os_module_status) && $os_module_status == '0') {
181
            echo "Module [ $module ] disabled on os.\n\n";
182
        } else {
183
            echo "Module [ $module ] disabled globally.\n\n";
184
        }
185
    }
186
187
    $device_time = round(microtime(true) - $device_start, 3);
188
189
    dbUpdate(['last_discovered' => ['NOW()'], 'last_discovered_timetaken' => $device_time], 'devices', '`device_id` = ?', [$device['device_id']]);
190
191
    echo "Discovered in $device_time seconds\n";
192
193
    echo PHP_EOL;
194
195
    return true;
196
}
197
//end discover_device()
198
199
// Discover sensors
200
function discover_sensor(&$valid, $class, $device, $oid, $index, $type, $descr, $divisor = 1, $multiplier = 1, $low_limit = null, $low_warn_limit = null, $warn_limit = null, $high_limit = null, $current = null, $poller_type = 'snmp', $entPhysicalIndex = null, $entPhysicalIndex_measured = null, $user_func = null, $group = null)
201
{
202
    $guess_limits = Config::get('sensors.guess_limits', true);
203
204
    $low_limit = set_null($low_limit);
205
    $low_warn_limit = set_null($low_warn_limit);
206
    $warn_limit = set_null($warn_limit);
207
    $high_limit = set_null($high_limit);
208
    $current = cast_number($current);
209
210
    if (! is_numeric($divisor)) {
211
        $divisor = 1;
212
    }
213
    if (can_skip_sensor($device, $class, $descr)) {
214
        return false;
215
    }
216
217
    d_echo("Discover sensor: $oid, $index, $type, $descr, $poller_type, $divisor, $multiplier, $entPhysicalIndex, $current, (limits: LL: $low_limit, LW: $low_warn_limit, W: $warn_limit, H: $high_limit)\n");
218
219
    if (isset($warn_limit, $low_warn_limit) && $low_warn_limit > $warn_limit) {
220
        // Fix high/low thresholds (i.e. on negative numbers)
221
        [$warn_limit, $low_warn_limit] = [$low_warn_limit, $warn_limit];
222
    }
223
224
    if (dbFetchCell('SELECT COUNT(sensor_id) FROM `sensors` WHERE `poller_type`= ? AND `sensor_class` = ? AND `device_id` = ? AND sensor_type = ? AND `sensor_index` = ?', [$poller_type, $class, $device['device_id'], $type, (string) $index]) == '0') {
225
        if ($guess_limits && is_null($high_limit)) {
226
            $high_limit = sensor_limit($class, $current);
227
        }
228
229
        if ($guess_limits && is_null($low_limit)) {
230
            $low_limit = sensor_low_limit($class, $current);
231
        }
232
233
        if (! is_null($high_limit) && $low_limit > $high_limit) {
234
            // Fix high/low thresholds (i.e. on negative numbers)
235
            [$high_limit, $low_limit] = [$low_limit, $high_limit];
236
        }
237
238
        $insert = [
239
            'poller_type' => $poller_type,
240
            'sensor_class' => $class,
241
            'device_id' => $device['device_id'],
242
            'sensor_oid' => $oid,
243
            'sensor_index' => $index,
244
            'sensor_type' => $type,
245
            'sensor_descr' => $descr,
246
            'sensor_divisor' => $divisor,
247
            'sensor_multiplier' => $multiplier,
248
            'sensor_limit' => $high_limit,
249
            'sensor_limit_warn' => $warn_limit,
250
            'sensor_limit_low' => $low_limit,
251
            'sensor_limit_low_warn' => $low_warn_limit,
252
            'sensor_current' => $current,
253
            'entPhysicalIndex' => $entPhysicalIndex,
254
            'entPhysicalIndex_measured' => $entPhysicalIndex_measured,
255
            'user_func' => $user_func,
256
            'group' => $group,
257
        ];
258
259
        foreach ($insert as $key => $val_check) {
260
            if (! isset($val_check)) {
261
                unset($insert[$key]);
262
            }
263
        }
264
265
        $inserted = dbInsert($insert, 'sensors');
266
267
        d_echo("( $inserted inserted )\n");
268
269
        echo '+';
270
        log_event('Sensor Added: ' . $class . ' ' . $type . ' ' . $index . ' ' . $descr, $device, 'sensor', 3, $inserted);
271
    } else {
272
        $sensor_entry = dbFetchRow('SELECT * FROM `sensors` WHERE `sensor_class` = ? AND `device_id` = ? AND `sensor_type` = ? AND `sensor_index` = ?', [$class, $device['device_id'], $type, (string) $index]);
273
274
        if (! isset($high_limit)) {
275
            if ($guess_limits && ! $sensor_entry['sensor_limit']) {
276
                // Calculate a reasonable limit
277
                $high_limit = sensor_limit($class, $current);
278
            } else {
279
                // Use existing limit
280
                $high_limit = $sensor_entry['sensor_limit'];
281
            }
282
        }
283
284
        if (! isset($low_limit)) {
285
            if ($guess_limits && ! $sensor_entry['sensor_limit_low']) {
286
                // Calculate a reasonable limit
287
                $low_limit = sensor_low_limit($class, $current);
288
            } else {
289
                // Use existing limit
290
                $low_limit = $sensor_entry['sensor_limit_low'];
291
            }
292
        }
293
294
        // Fix high/low thresholds (i.e. on negative numbers)
295
        if (isset($high_limit) && $low_limit > $high_limit) {
296
            [$high_limit, $low_limit] = [$low_limit, $high_limit];
297
        }
298
299
        if ($high_limit != $sensor_entry['sensor_limit'] && $sensor_entry['sensor_custom'] == 'No') {
300
            $update = ['sensor_limit' => ($high_limit == null ? ['NULL'] : $high_limit)];
301
            $updated = dbUpdate($update, 'sensors', '`sensor_id` = ?', [$sensor_entry['sensor_id']]);
302
            d_echo("( $updated updated )\n");
303
304
            echo 'H';
305
            log_event('Sensor High Limit Updated: ' . $class . ' ' . $type . ' ' . $index . ' ' . $descr . ' (' . $high_limit . ')', $device, 'sensor', 3, $sensor_entry['sensor_id']);
306
        }
307
308
        if ($sensor_entry['sensor_limit_low'] != $low_limit && $sensor_entry['sensor_custom'] == 'No') {
309
            $update = ['sensor_limit_low' => ($low_limit == null ? ['NULL'] : $low_limit)];
310
            $updated = dbUpdate($update, 'sensors', '`sensor_id` = ?', [$sensor_entry['sensor_id']]);
311
            d_echo("( $updated updated )\n");
312
313
            echo 'L';
314
            log_event('Sensor Low Limit Updated: ' . $class . ' ' . $type . ' ' . $index . ' ' . $descr . ' (' . $low_limit . ')', $device, 'sensor', 3, $sensor_entry['sensor_id']);
315
        }
316
317
        if ($warn_limit != $sensor_entry['sensor_limit_warn'] && $sensor_entry['sensor_custom'] == 'No') {
318
            $update = ['sensor_limit_warn' => ($warn_limit == null ? ['NULL'] : $warn_limit)];
319
            $updated = dbUpdate($update, 'sensors', '`sensor_id` = ?', [$sensor_entry['sensor_id']]);
320
            d_echo("( $updated updated )\n");
321
322
            echo 'WH';
323
            log_event('Sensor Warn High Limit Updated: ' . $class . ' ' . $type . ' ' . $index . ' ' . $descr . ' (' . $warn_limit . ')', $device, 'sensor', 3, $sensor_entry['sensor_id']);
324
        }
325
326
        if ($sensor_entry['sensor_limit_low_warn'] != $low_warn_limit && $sensor_entry['sensor_custom'] == 'No') {
327
            $update = ['sensor_limit_low_warn' => ($low_warn_limit == null ? ['NULL'] : $low_warn_limit)];
328
            $updated = dbUpdate($update, 'sensors', '`sensor_id` = ?', [$sensor_entry['sensor_id']]);
329
            d_echo("( $updated updated )\n");
330
331
            echo 'WL';
332
            log_event('Sensor Warn Low Limit Updated: ' . $class . ' ' . $type . ' ' . $index . ' ' . $descr . ' (' . $low_warn_limit . ')', $device, 'sensor', 3, $sensor_entry['sensor_id']);
333
        }
334
335
        if ($oid == $sensor_entry['sensor_oid'] &&
336
            $descr == $sensor_entry['sensor_descr'] &&
337
            $multiplier == $sensor_entry['sensor_multiplier'] &&
338
            $divisor == $sensor_entry['sensor_divisor'] &&
339
            $entPhysicalIndex_measured == $sensor_entry['entPhysicalIndex_measured'] &&
340
            $entPhysicalIndex == $sensor_entry['entPhysicalIndex'] &&
341
            $user_func == $sensor_entry['user_func'] &&
342
            $group == $sensor_entry['group']
343
344
        ) {
345
            echo '.';
346
        } else {
347
            $update = [
348
                'sensor_oid' => $oid,
349
                'sensor_descr' => $descr,
350
                'sensor_multiplier' => $multiplier,
351
                'sensor_divisor' => $divisor,
352
                'entPhysicalIndex' => $entPhysicalIndex,
353
                'entPhysicalIndex_measured' => $entPhysicalIndex_measured,
354
                'user_func' => $user_func,
355
                'group' => $group,
356
            ];
357
            $updated = dbUpdate($update, 'sensors', '`sensor_id` = ?', [$sensor_entry['sensor_id']]);
358
            echo 'U';
359
            log_event('Sensor Updated: ' . $class . ' ' . $type . ' ' . $index . ' ' . $descr, $device, 'sensor', 3, $sensor_entry['sensor_id']);
360
            d_echo("( $updated updated )\n");
361
        }
362
    }//end if
363
    $valid[$class][$type][$index] = 1;
364
}
365
366
//end discover_sensor()
367
368
function sensor_low_limit($class, $current)
369
{
370
    // matching an empty case executes code until a break is reached
371
    switch ($class) {
372
        case 'temperature':
373
            $limit = $current - 10;
374
            break;
375
        case 'voltage':
376
            $limit = $current * 0.85;
377
            break;
378
        case 'humidity':
379
            $limit = 30;
380
            break;
381
        case 'fanspeed':
382
            $limit = $current * 0.80;
383
            break;
384
        case 'power_factor':
385
            $limit = -1;
386
            break;
387
        case 'signal':
388
            $limit = -80;
389
            break;
390
        case 'airflow':
391
        case 'snr':
392
        case 'frequency':
393
        case 'pressure':
394
        case 'cooling':
395
            $limit = $current * 0.95;
396
            break;
397
        default:
398
            return null;
399
    }//end switch
400
401
    return round($limit, 11);
402
}
403
404
//end sensor_low_limit()
405
406
function sensor_limit($class, $current)
407
{
408
    // matching an empty case executes code until a break is reached
409
    switch ($class) {
410
        case 'temperature':
411
            $limit = $current + 20;
412
            break;
413
        case 'voltage':
414
            $limit = $current * 1.15;
415
            break;
416
        case 'humidity':
417
            $limit = 70;
418
            break;
419
        case 'fanspeed':
420
            $limit = $current * 1.80;
421
            break;
422
        case 'power_factor':
423
            $limit = 1;
424
            break;
425
        case 'signal':
426
            $limit = -30;
427
            break;
428
        case 'load':
429
            $limit = 80;
430
            break;
431
        case 'airflow':
432
        case 'snr':
433
        case 'frequency':
434
        case 'pressure':
435
        case 'cooling':
436
            $limit = $current * 1.05;
437
            break;
438
        default:
439
            return null;
440
    }//end switch
441
442
    return round($limit, 11);
443
}
444
445
//end sensor_limit()
446
447
function check_valid_sensors($device, $class, $valid, $poller_type = 'snmp')
448
{
449
    $entries = dbFetchRows('SELECT * FROM sensors AS S, devices AS D WHERE S.sensor_class=? AND S.device_id = D.device_id AND D.device_id = ? AND S.poller_type = ?', [$class, $device['device_id'], $poller_type]);
450
451
    if (count($entries)) {
452
        foreach ($entries as $entry) {
453
            $index = $entry['sensor_index'];
454
            $type = $entry['sensor_type'];
455
            $class = $entry['sensor_class'];
456
            d_echo($index . ' -> ' . $type . "\n");
457
458
            if (! $valid[$class][$type][$index]) {
459
                echo '-';
460
                if ($class == 'state') {
461
                    dbDelete('sensors_to_state_indexes', '`sensor_id` =  ?', [$entry['sensor_id']]);
462
                }
463
                dbDelete('sensors', '`sensor_id` =  ?', [$entry['sensor_id']]);
464
                log_event('Sensor Deleted: ' . $entry['sensor_class'] . ' ' . $entry['sensor_type'] . ' ' . $entry['sensor_index'] . ' ' . $entry['sensor_descr'], $device, 'sensor', 3, $entry['sensor_id']);
465
            }
466
467
            unset($oid);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $oid seems to be never defined.
Loading history...
468
            unset($type);
469
        }
470
    }
471
}
472
473
//end check_valid_sensors()
474
475
function discover_juniAtmVp(&$valid, $device, $port_id, $vp_id, $vp_descr)
476
{
477
    d_echo("Discover Juniper ATM VP: $port_id, $vp_id, $vp_descr\n");
478
479
    if (dbFetchCell('SELECT COUNT(*) FROM `juniAtmVp` WHERE `port_id` = ? AND `vp_id` = ?', [$port_id, $vp_id]) == '0') {
480
        $inserted = dbInsert(['port_id' => $port_id, 'vp_id' => $vp_id, 'vp_descr' => $vp_descr], 'juniAtmVp');
481
        d_echo("( $inserted inserted )\n");
482
483
        // FIXME vv no $device!
484
        log_event('Juniper ATM VP Added: port ' . $port_id . ' vp ' . $vp_id . ' descr' . $vp_descr, $device, 'juniAtmVp', 3, $inserted);
485
    } else {
486
        echo '.';
487
    }
488
489
    $valid[$port_id][$vp_id] = 1;
490
}
491
492
//end discover_juniAtmVp()
493
494
function discover_link($local_port_id, $protocol, $remote_port_id, $remote_hostname, $remote_port, $remote_platform, $remote_version, $local_device_id, $remote_device_id)
495
{
496
    global $link_exists;
497
498
    d_echo("Discover link: $local_port_id, $protocol, $remote_port_id, $remote_hostname, $remote_port, $remote_platform, $remote_version, $remote_device_id\n");
499
500
    if (dbFetchCell(
501
        'SELECT COUNT(*) FROM `links` WHERE `remote_hostname` = ? AND `local_port_id` = ? AND `protocol` = ? AND `remote_port` = ?',
502
        [
503
            $remote_hostname,
504
            $local_port_id,
505
            $protocol,
506
            $remote_port,
507
        ]
508
    ) == '0') {
509
        $insert_data = [
510
            'local_port_id' => $local_port_id,
511
            'local_device_id' => $local_device_id,
512
            'protocol' => $protocol,
513
            'remote_hostname' => $remote_hostname,
514
            'remote_device_id' => (int) $remote_device_id,
515
            'remote_port' => $remote_port,
516
            'remote_platform' => $remote_platform,
517
            'remote_version' => $remote_version,
518
        ];
519
520
        if (! empty($remote_port_id)) {
521
            $insert_data['remote_port_id'] = (int) $remote_port_id;
522
        }
523
524
        $inserted = dbInsert($insert_data, 'links');
525
526
        echo '+';
527
        d_echo("( $inserted inserted )");
528
    } else {
529
        $sql = 'SELECT `id`,`local_device_id`,`remote_platform`,`remote_version`,`remote_device_id`,`remote_port_id` FROM `links`';
530
        $sql .= ' WHERE `remote_hostname` = ? AND `local_port_id` = ? AND `protocol` = ? AND `remote_port` = ?';
531
        $data = dbFetchRow($sql, [$remote_hostname, $local_port_id, $protocol, $remote_port]);
532
533
        $update_data = [
534
            'local_device_id' => $local_device_id,
535
            'remote_platform' => $remote_platform,
536
            'remote_version' => $remote_version,
537
            'remote_device_id' => (int) $remote_device_id,
538
            'remote_port_id' => (int) $remote_port_id,
539
        ];
540
541
        $id = $data['id'];
542
        unset($data['id']);
543
        if ($data == $update_data) {
544
            echo '.';
545
        } else {
546
            $updated = dbUpdate($update_data, 'links', '`id` = ?', [$id]);
547
            echo 'U';
548
            d_echo("( $updated updated )");
549
        }//end if
550
    }//end if
551
    $link_exists[$local_port_id][$remote_hostname][$remote_port] = 1;
552
}
553
554
//end discover_link()
555
556
function discover_storage(&$valid, $device, $index, $type, $mib, $descr, $size, $units, $used = null)
557
{
558
    if (ignore_storage($device['os'], $descr)) {
559
        return;
560
    }
561
    d_echo("Discover Storage: $index, $type, $mib, $descr, $size, $units, $used\n");
562
563
    if ($descr && $size > '0') {
564
        $storage = dbFetchRow('SELECT * FROM `storage` WHERE `storage_index` = ? AND `device_id` = ? AND `storage_mib` = ?', [$index, $device['device_id'], $mib]);
565
        if (empty($storage)) {
566
            if (Config::getOsSetting($device['os'], 'storage_perc_warn')) {
567
                $perc_warn = Config::getOsSetting($device['os'], 'storage_perc_warn');
568
            } else {
569
                $perc_warn = Config::get('storage_perc_warn', 60);
570
            }
571
572
            dbInsert(
573
                [
574
                    'device_id' => $device['device_id'],
575
                    'storage_descr' => $descr,
576
                    'storage_index' => $index,
577
                    'storage_mib' => $mib,
578
                    'storage_type' => $type,
579
                    'storage_units' => $units,
580
                    'storage_size' => $size,
581
                    'storage_used' => $used,
582
                    'storage_perc_warn' => $perc_warn,
583
                ],
584
                'storage'
585
            );
586
587
            echo '+';
588
        } else {
589
            $updated = dbUpdate(['storage_descr' => $descr, 'storage_type' => $type, 'storage_units' => $units, 'storage_size' => $size], 'storage', '`device_id` = ? AND `storage_index` = ? AND `storage_mib` = ?', [$device['device_id'], $index, $mib]);
590
            if ($updated) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $updated of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
591
                echo 'U';
592
            } else {
593
                echo '.';
594
            }
595
        }//end if
596
597
        $valid[$mib][$index] = 1;
598
    }//end if
599
}
600
601
function discover_entity_physical(&$valid, $device, $entPhysicalIndex, $entPhysicalDescr, $entPhysicalClass, $entPhysicalName, $entPhysicalModelName, $entPhysicalSerialNum, $entPhysicalContainedIn, $entPhysicalMfgName, $entPhysicalParentRelPos, $entPhysicalVendorType, $entPhysicalHardwareRev, $entPhysicalFirmwareRev, $entPhysicalSoftwareRev, $entPhysicalIsFRU, $entPhysicalAlias, $entPhysicalAssetID, $ifIndex)
602
{
603
    d_echo("Discover Inventory Item: $entPhysicalIndex, $entPhysicalDescr, $entPhysicalClass, $entPhysicalName, $entPhysicalModelName, $entPhysicalSerialNum, $entPhysicalContainedIn, $entPhysicalMfgName, $entPhysicalParentRelPos, $entPhysicalVendorType, $entPhysicalHardwareRev, $entPhysicalFirmwareRev, $entPhysicalSoftwareRev, $entPhysicalIsFRU, $entPhysicalAlias, $entPhysicalAssetID, $ifIndex\n");
604
605
    if ($entPhysicalDescr || $entPhysicalName) {
606
        if (dbFetchCell('SELECT COUNT(entPhysical_id) FROM `entPhysical` WHERE `device_id` = ? AND `entPhysicalIndex` = ?', [$device['device_id'], $entPhysicalIndex]) == '0') {
607
            $insert_data = [
608
                'device_id'               => $device['device_id'],
609
                'entPhysicalIndex'        => $entPhysicalIndex,
610
                'entPhysicalDescr'        => $entPhysicalDescr,
611
                'entPhysicalClass'        => $entPhysicalClass,
612
                'entPhysicalName'         => $entPhysicalName,
613
                'entPhysicalModelName'    => $entPhysicalModelName,
614
                'entPhysicalSerialNum'    => $entPhysicalSerialNum,
615
                'entPhysicalContainedIn'  => $entPhysicalContainedIn,
616
                'entPhysicalMfgName'      => $entPhysicalMfgName,
617
                'entPhysicalParentRelPos' => $entPhysicalParentRelPos,
618
                'entPhysicalVendorType'   => $entPhysicalVendorType,
619
                'entPhysicalHardwareRev'  => $entPhysicalHardwareRev,
620
                'entPhysicalFirmwareRev'  => $entPhysicalFirmwareRev,
621
                'entPhysicalSoftwareRev'  => $entPhysicalSoftwareRev,
622
                'entPhysicalIsFRU'        => $entPhysicalIsFRU,
623
                'entPhysicalAlias'        => $entPhysicalAlias,
624
                'entPhysicalAssetID'      => $entPhysicalAssetID,
625
            ];
626
            if (! empty($ifIndex)) {
627
                $insert_data['ifIndex'] = $ifIndex;
628
            }
629
630
            $inserted = dbInsert($insert_data, 'entPhysical');
631
            echo '+';
632
            log_event('Inventory Item added: index ' . $entPhysicalIndex . ' descr ' . $entPhysicalDescr, $device, 'entity-physical', 3, $inserted);
633
        } else {
634
            echo '.';
635
            $update_data = [
636
                'entPhysicalIndex'        => $entPhysicalIndex,
637
                'entPhysicalDescr'        => $entPhysicalDescr,
638
                'entPhysicalClass'        => $entPhysicalClass,
639
                'entPhysicalName'         => $entPhysicalName,
640
                'entPhysicalModelName'    => $entPhysicalModelName,
641
                'entPhysicalSerialNum'    => $entPhysicalSerialNum,
642
                'entPhysicalContainedIn'  => $entPhysicalContainedIn,
643
                'entPhysicalMfgName'      => $entPhysicalMfgName,
644
                'entPhysicalParentRelPos' => $entPhysicalParentRelPos,
645
                'entPhysicalVendorType'   => $entPhysicalVendorType,
646
                'entPhysicalHardwareRev'  => $entPhysicalHardwareRev,
647
                'entPhysicalFirmwareRev'  => $entPhysicalFirmwareRev,
648
                'entPhysicalSoftwareRev'  => $entPhysicalSoftwareRev,
649
                'entPhysicalIsFRU'        => $entPhysicalIsFRU,
650
                'entPhysicalAlias'        => $entPhysicalAlias,
651
                'entPhysicalAssetID'      => $entPhysicalAssetID,
652
                'ifIndex'                 => $ifIndex,
653
            ];
654
            dbUpdate($update_data, 'entPhysical', '`device_id`=? AND `entPhysicalIndex`=?', [$device['device_id'], $entPhysicalIndex]);
655
        }//end if
656
        $valid[$entPhysicalIndex] = 1;
657
    }//end if
658
}
659
660
//end discover_entity_physical()
661
662
function discover_process_ipv6(&$valid, $ifIndex, $ipv6_address, $ipv6_prefixlen, $ipv6_origin, $context_name = '')
663
{
664
    global $device;
665
666
    if (! IPv6::isValid($ipv6_address, true)) {
667
        // ignore link-locals (coming from IPV6-MIB)
668
        return;
669
    }
670
671
    $ipv6 = new IPv6($ipv6_address);
672
    $ipv6_network = $ipv6->getNetwork($ipv6_prefixlen);
673
    $ipv6_compressed = $ipv6->compressed();
674
675
    if (dbFetchCell('SELECT COUNT(*) FROM `ports` WHERE device_id = ? AND `ifIndex` = ?', [$device['device_id'], $ifIndex]) != '0' && $ipv6_prefixlen > '0' && $ipv6_prefixlen < '129' && $ipv6_compressed != '::1') {
676
        $port_id = dbFetchCell('SELECT port_id FROM `ports` WHERE device_id = ? AND ifIndex = ?', [$device['device_id'], $ifIndex]);
677
678
        if (is_numeric($port_id)) {
679
            if (dbFetchCell('SELECT COUNT(*) FROM `ipv6_networks` WHERE `ipv6_network` = ?', [$ipv6_network]) < '1') {
680
                dbInsert(['ipv6_network' => $ipv6_network, 'context_name' => $context_name], 'ipv6_networks');
681
                echo 'N';
682
            } else {
683
                //Update Context
684
                dbUpdate(['context_name' => $device['context_name']], 'ipv6_networks', '`ipv6_network` = ?', [$ipv6_network]);
685
                echo 'n';
686
            }
687
688
            if ($context_name == null) {
689
                $ipv6_network_id = dbFetchCell('SELECT `ipv6_network_id` FROM `ipv6_networks` WHERE `ipv6_network` = ? AND `context_name` IS NULL', [$ipv6_network]);
690
            } else {
691
                $ipv6_network_id = dbFetchCell('SELECT `ipv6_network_id` FROM `ipv6_networks` WHERE `ipv6_network` = ? AND `context_name` = ?', [$ipv6_network, $context_name]);
692
            }
693
            if (dbFetchCell('SELECT COUNT(*) FROM `ipv6_addresses` WHERE `ipv6_address` = ? AND `ipv6_prefixlen` = ? AND `port_id` = ?', [$ipv6_address, $ipv6_prefixlen, $port_id]) == '0') {
694
                dbInsert([
695
                    'ipv6_address' => $ipv6_address,
696
                    'ipv6_compressed' => $ipv6_compressed,
697
                    'ipv6_prefixlen' => $ipv6_prefixlen,
698
                    'ipv6_origin' => $ipv6_origin,
699
                    'ipv6_network_id' => $ipv6_network_id,
700
                    'port_id' => $port_id,
701
                    'context_name' => $context_name,
702
                ], 'ipv6_addresses');
703
                echo '+';
704
            } elseif (dbFetchCell('SELECT COUNT(*) FROM `ipv6_addresses` WHERE `ipv6_address` = ? AND `ipv6_prefixlen` = ? AND `port_id` = ? AND `ipv6_network_id` = ""', [$ipv6_address, $ipv6_prefixlen, $port_id]) == '1') {
705
                // Update IPv6 network ID if not set
706
                if ($context_name == null) {
707
                    $ipv6_network_id = dbFetchCell('SELECT `ipv6_network_id` FROM `ipv6_networks` WHERE `ipv6_network` = ? AND `context_name` IS NULL', [$ipv6_network]);
708
                } else {
709
                    $ipv6_network_id = dbFetchCell('SELECT `ipv6_network_id` FROM `ipv6_networks` WHERE `ipv6_network` = ? AND `context_name` = ?', [$ipv6_network, $context_name]);
710
                }
711
                dbUpdate(['ipv6_network_id' => $ipv6_network_id], 'ipv6_addresses', '`ipv6_address` = ? AND `ipv6_prefixlen` = ? AND `port_id` = ?', [$ipv6_address, $ipv6_prefixlen, $port_id]);
712
                echo 'u';
713
            } else {
714
                //Update Context
715
                dbUpdate(['context_name' => $device['context_name']], 'ipv6_addresses', '`ipv6_address` = ? AND `ipv6_prefixlen` = ? AND `port_id` = ?', [$ipv6_address, $ipv6_prefixlen, $port_id]);
716
                echo '.';
717
            }
718
719
            $full_address = "$ipv6_address/$ipv6_prefixlen";
720
            $valid_address = $full_address . '-' . $port_id;
721
            $valid['ipv6'][$valid_address] = 1;
722
        }
723
    }//end if
724
}//end discover_process_ipv6()
725
726
/*
727
 * Check entity sensors to be excluded
728
 *
729
 * @param string value to check
730
 * @param array device
731
 *
732
 * @return bool true if sensor is valid
733
 *              false if sensor is invalid
734
*/
735
function check_entity_sensor($string, $device)
736
{
737
    $fringe = array_merge(Config::get('bad_entity_sensor_regex', []), Config::getOsSetting($device['os'], 'bad_entity_sensor_regex', []));
738
739
    foreach ($fringe as $bad) {
740
        if (preg_match($bad . 'i', $string)) {
741
            d_echo("Ignored entity sensor: $bad : $string");
742
743
            return false;
744
        }
745
    }
746
747
    return true;
748
}
749
750
/**
751
 * Get the device divisor, account for device specific quirks
752
 * The default divisor is 10
753
 *
754
 * @param  array  $device  device array
755
 * @param  string  $os_version  firmware version poweralert quirks
756
 * @param  string  $sensor_type  the type of this sensor
757
 * @param  string  $oid  the OID of this sensor
758
 * @return int
759
 */
760
function get_device_divisor($device, $os_version, $sensor_type, $oid)
761
{
762
    if ($device['os'] == 'poweralert') {
763
        if ($sensor_type == 'current' || $sensor_type == 'frequency') {
764
            if (version_compare($os_version, '12.06.0068', '>=')) {
765
                return 10;
766
            } elseif (version_compare($os_version, '12.04.0055', '=')) {
767
                return 10;
768
            } elseif (version_compare($os_version, '12.04.0056', '>=')) {
769
                return 1;
770
            }
771
        } elseif ($sensor_type == 'load') {
772
            if (version_compare($os_version, '12.06.0064', '=')) {
773
                return 10;
774
            } else {
775
                return 1;
776
            }
777
        }
778
    } elseif ($device['os'] == 'huaweiups') {
779
        if ($sensor_type == 'frequency') {
780
            if (Str::startsWith($device['hardware'], 'UPS2000')) {
781
                return 10;
782
            }
783
784
            return 100;
785
        }
786
    } elseif ($device['os'] == 'hpe-rtups') {
787
        if ($sensor_type == 'voltage' && ! Str::startsWith($oid, '.1.3.6.1.2.1.33.1.2.5.') && ! Str::startsWith($oid, '.1.3.6.1.2.1.33.1.3.3.1.3')) {
788
            return 1;
789
        }
790
    } elseif ($device['os'] == 'apc-mgeups') {
791
        if ($sensor_type == 'voltage') {
792
            return 10;
793
        }
794
    }
795
796
    // UPS-MIB Defaults
797
798
    if ($sensor_type == 'load') {
799
        return 1;
800
    }
801
802
    if ($sensor_type == 'voltage' && ! Str::startsWith($oid, '.1.3.6.1.2.1.33.1.2.5.')) {
803
        return 1;
804
    }
805
806
    if ($sensor_type == 'runtime') {
807
        if (Str::startsWith($oid, '.1.3.6.1.2.1.33.1.2.2.')) {
808
            return 60;
809
        }
810
811
        if (Str::startsWith($oid, '.1.3.6.1.2.1.33.1.2.3.')) {
812
            if ($device['os'] == 'routeros') {
813
                return 60;
814
            } else {
815
                return 1;
816
            }
817
        }
818
    }
819
820
    return 10;
821
}
822
823
/**
824
 * Should we ignore this storage device based on teh description? (usually the mount path or drive)
825
 *
826
 * @param  string  $os  The OS of the device
827
 * @param  string  $descr  The description of the storage
828
 * @return bool
829
 */
830
function ignore_storage($os, $descr)
831
{
832
    foreach (Config::getCombined($os, 'ignore_mount', []) as $im) {
833
        if ($im == $descr) {
834
            d_echo("ignored $descr (matched: $im)\n");
835
836
            return true;
837
        }
838
    }
839
840
    foreach (Config::getCombined($os, 'ignore_mount_string', []) as $ims) {
841
        if (Str::contains($descr, $ims)) {
842
            d_echo("ignored $descr (matched: $ims)\n");
843
844
            return true;
845
        }
846
    }
847
848
    foreach (Config::getCombined($os, 'ignore_mount_regexp', []) as $imr) {
849
        if (preg_match($imr, $descr)) {
850
            d_echo("ignored $descr (matched: $imr)\n");
851
852
            return true;
853
        }
854
    }
855
856
    return false;
857
}
858
859
/**
860
 * @param $valid
861
 * @param $device
862
 * @param $sensor_type
863
 * @param $pre_cache
864
 */
865
function discovery_process(&$valid, $device, $sensor_class, $pre_cache)
866
{
867
    if (! empty($device['dynamic_discovery']['modules']['sensors'][$sensor_class]) && ! can_skip_sensor($device, $sensor_class, '')) {
868
        $sensor_options = [];
869
        if (isset($device['dynamic_discovery']['modules']['sensors'][$sensor_class]['options'])) {
870
            $sensor_options = $device['dynamic_discovery']['modules']['sensors'][$sensor_class]['options'];
871
        }
872
873
        d_echo("Dynamic Discovery ($sensor_class): ");
874
        d_echo($device['dynamic_discovery']['modules']['sensors'][$sensor_class]);
875
876
        foreach ($device['dynamic_discovery']['modules']['sensors'][$sensor_class]['data'] as $data) {
877
            $tmp_name = $data['oid'];
878
            $raw_data = (array) $pre_cache[$tmp_name];
879
880
            d_echo("Data $tmp_name: ");
881
            d_echo($raw_data);
882
883
            foreach ($raw_data as $index => $snmp_data) {
884
                $user_function = null;
885
                if (isset($data['user_func'])) {
886
                    $user_function = $data['user_func'];
887
                }
888
                // get the value for this sensor, check 'value' and 'oid', if state string, translate to a number
889
                $data['value'] = isset($data['value']) ? $data['value'] : $data['oid'];  // fallback to oid if value is not set
890
891
                $snmp_value = $snmp_data[$data['value']];
892
                if (! is_numeric($snmp_value)) {
893
                    if ($sensor_class === 'temperature') {
894
                        // For temp sensors, try and detect fahrenheit values
895
                        if (Str::endsWith($snmp_value, ['f', 'F'])) {
896
                            $user_function = 'fahrenheit_to_celsius';
897
                        }
898
                    }
899
                    preg_match('/-?\d*\.?\d+/', $snmp_value, $temp_response);
900
                    if (! empty($temp_response[0])) {
901
                        $snmp_value = $temp_response[0];
902
                    }
903
                }
904
905
                if (is_numeric($snmp_value)) {
906
                    $value = $snmp_value;
907
                } elseif ($sensor_class === 'state') {
908
                    // translate string states to values (poller does this as well)
909
                    $states = array_column($data['states'], 'value', 'descr');
910
                    $value = isset($states[$snmp_value]) ? $states[$snmp_value] : false;
911
                } else {
912
                    $value = false;
913
                }
914
915
                $skippedFromYaml = YamlDiscovery::canSkipItem($value, $index, $data, $sensor_options, $pre_cache);
916
917
                // Check if we have a "num_oid" value. If not, we'll try to compute it from textual OIDs with snmptranslate.
918
                if (empty($data['num_oid'])) {
919
                    try {
920
                        $data['num_oid'] = YamlDiscovery::computeNumericalOID($device, $data);
921
                    } catch (\Exception $e) {
922
                        d_echo('Error: We cannot find a numerical OID for ' . $data['value'] . '. Skipping this one...');
923
                        $skippedFromYaml = true;
924
                        // Because we don't have a num_oid, we have no way to add this sensor.
925
                    }
926
                }
927
928
                if ($skippedFromYaml === false && is_numeric($value)) {
929
                    d_echo("Sensor fetched value: $value\n");
930
931
                    $oid = str_replace('{{ $index }}', $index, $data['num_oid']);
932
                    // if index is a string, we need to convert it to OID
933
                    // strlen($index) as first number, and each letter converted to a number, separated by dots
934
                    $oid = str_replace('{{ $index_string }}', strlen($index) . '.' . implode('.', unpack('c*', $index)), $oid);
935
936
                    // process the description
937
                    $descr = YamlDiscovery::replaceValues('descr', $index, null, $data, $pre_cache);
938
939
                    // process the group
940
                    $group = YamlDiscovery::replaceValues('group', $index, null, $data, $pre_cache) ?: null;
941
942
                    $divisor = $data['divisor'] ?? ($sensor_options['divisor'] ?? 1);
943
                    $multiplier = $data['multiplier'] ?? ($sensor_options['multiplier'] ?? 1);
944
945
                    $limits = ['low_limit', 'low_warn_limit', 'warn_limit', 'high_limit'];
946
                    foreach ($limits as $limit) {
947
                        if (isset($data[$limit]) && is_numeric($data[$limit])) {
948
                            $$limit = $data[$limit];
949
                        } else {
950
                            $$limit = YamlDiscovery::getValueFromData($limit, $index, $data, $pre_cache, 'null');
951
                            if (is_numeric($$limit)) {
952
                                $$limit = ($$limit / $divisor) * $multiplier;
953
                            }
954
                            if (is_numeric($$limit) && isset($user_function) && is_callable($user_function)) {
955
                                $$limit = $user_function($$limit);
956
                            }
957
                        }
958
                    }
959
960
                    $sensor_name = $device['os'];
961
962
                    if ($sensor_class === 'state') {
963
                        $sensor_name = $data['state_name'] ?: $data['oid'];
964
                        create_state_index($sensor_name, $data['states']);
965
                    } else {
966
                        // We default to 1 for both divisors / multipliers so it should be safe to do the calculation using both.
967
                        $value = ($value / $divisor) * $multiplier;
968
                    }
969
970
                    echo "Cur $value, Low: $low_limit, Low Warn: $low_warn_limit, Warn: $warn_limit, High: $high_limit" . PHP_EOL;
971
                    $entPhysicalIndex = YamlDiscovery::replaceValues('entPhysicalIndex', $index, null, $data, $pre_cache) ?: null;
972
                    $entPhysicalIndex_measured = isset($data['entPhysicalIndex_measured']) ? $data['entPhysicalIndex_measured'] : null;
973
974
                    //user_func must be applied after divisor/multiplier
975
                    if (isset($user_function) && is_callable($user_function)) {
976
                        $value = $user_function($value);
977
                    }
978
979
                    $uindex = str_replace('{{ $index }}', $index, isset($data['index']) ? $data['index'] : $index);
980
                    discover_sensor($valid['sensor'], $sensor_class, $device, $oid, $uindex, $sensor_name, $descr, $divisor, $multiplier, $low_limit, $low_warn_limit, $warn_limit, $high_limit, $value, 'snmp', $entPhysicalIndex, $entPhysicalIndex_measured, $user_function, $group);
981
982
                    if ($sensor_class === 'state') {
983
                        create_sensor_to_state_index($device, $sensor_name, $uindex);
984
                    }
985
                }
986
            }
987
        }
988
    }
989
}
990
991
/**
992
 * @param $types
993
 * @param $device
994
 * @param  array  $pre_cache
995
 */
996
function sensors($types, $device, $valid, $pre_cache = [])
997
{
998
    foreach ((array) $types as $sensor_class) {
999
        echo ucfirst($sensor_class) . ': ';
1000
        $dir = Config::get('install_dir') . '/includes/discovery/sensors/' . $sensor_class . '/';
1001
1002
        if (isset($device['os_group']) && is_file($dir . $device['os_group'] . '.inc.php')) {
1003
            include $dir . $device['os_group'] . '.inc.php';
1004
        }
1005
        if (is_file($dir . $device['os'] . '.inc.php')) {
1006
            include $dir . $device['os'] . '.inc.php';
1007
        }
1008
        if (Config::getOsSetting($device['os'], 'rfc1628_compat', false)) {
1009
            if (is_file($dir . '/rfc1628.inc.php')) {
1010
                include $dir . '/rfc1628.inc.php';
1011
            }
1012
        }
1013
        discovery_process($valid, $device, $sensor_class, $pre_cache);
1014
        d_echo($valid['sensor'][$sensor_class] ?? []);
1015
        check_valid_sensors($device, $sensor_class, $valid['sensor']);
1016
        echo "\n";
1017
    }
1018
}
1019
1020
function build_bgp_peers($device, $data, $peer2)
1021
{
1022
    d_echo("Peers : $data\n");
1023
    $remove = [
1024
        'ARISTA-BGP4V2-MIB::aristaBgp4V2PeerRemoteAs.1.',
1025
        'ALCATEL-IND1-BGP-MIB::alaBgpPeerAS.',
1026
        'CISCO-BGP4-MIB::cbgpPeer2RemoteAs.',
1027
        'BGP4-MIB::bgpPeerRemoteAs.',
1028
        'HUAWEI-BGP-VPN-MIB::hwBgpPeerRemoteAs.',
1029
        '.1.3.6.1.4.1.2636.5.1.1.2.1.1.1.13.',
1030
    ];
1031
    $peers = trim(str_replace($remove, '', $data));
1032
1033
    $peerlist = [];
1034
    $ver = '';
1035
    foreach (explode("\n", $peers) as $peer) {
1036
        $local_ip = null;
1037
        if ($peer2 === true) {
1038
            [$ver, $peer] = explode('.', $peer, 2);
1039
        }
1040
        [$peer_ip, $peer_as] = explode(' ', $peer);
1041
        if ($device['os'] === 'junos') {
1042
            $ver = '';
1043
            $octets = count(explode('.', $peer_ip));
1044
            if ($octets > 11) {
1045
                // ipv6
1046
                $peer_ip = (string) IP::parse(snmp2ipv6($peer_ip), true);
1047
            } else {
1048
                // ipv4
1049
                $peer_ip = implode('.', array_slice(explode('.', $peer_ip), -4));
1050
            }
1051
        } else {
1052
            if (strstr($peer_ip, ':')) {
1053
                $peer_ip_snmp = preg_replace('/:/', ' ', $peer_ip);
1054
                $peer_ip = preg_replace('/(\S+\s+\S+)\s/', '$1:', $peer_ip_snmp);
1055
                $peer_ip = str_replace('"', '', str_replace(' ', '', $peer_ip));
1056
            }
1057
        }
1058
        if ($peer && $peer_ip != '0.0.0.0') {
1059
            if ($peer_as < 0) {
1060
                //if ASN is negative -> overflow int32 -> original number is max(INT32) - min(INT32) + 1 + value
1061
                $peer_as = 4294967296 + $peer_as;
1062
            }
1063
            d_echo("Found peer $peer_ip (AS$peer_as)\n");
1064
            $peerlist[] = [
1065
                'ip'      => $peer_ip,
1066
                'as'      => $peer_as,
1067
                'localip' => $local_ip ?: '0.0.0.0',
1068
                'ver'     => $ver,
1069
            ];
1070
        }
1071
    }
1072
1073
    return $peerlist;
1074
}
1075
1076
function build_cbgp_peers($device, $peer, $af_data, $peer2)
1077
{
1078
    d_echo('afi data :: ');
1079
    d_echo($af_data);
1080
1081
    $af_list = [];
1082
    foreach ($af_data as $k => $v) {
1083
        if ($peer2 === true) {
1084
            [,$k] = explode('.', $k, 2);
1085
        }
1086
1087
        d_echo("AFISAFI = $k\n");
1088
1089
        $afisafi_tmp = explode('.', $k);
1090
        if ($device['os_group'] === 'vrp') {
1091
            $vpninst_id = array_shift($afisafi_tmp);
1092
            $afi = array_shift($afisafi_tmp);
1093
            $safi = array_shift($afisafi_tmp);
1094
            $peertype = array_shift($afisafi_tmp);
1095
            $bgp_ip = implode('.', $afisafi_tmp);
1096
        } elseif ($device['os'] == 'aos7') {
1097
            $afi = 'ipv4';
1098
            $safi = 'unicast';
1099
            $bgp_ip = $k;
1100
        } else {
1101
            $safi = array_pop($afisafi_tmp);
1102
            $afi = array_pop($afisafi_tmp);
1103
            $bgp_ip = str_replace(".$afi.$safi", '', $k);
1104
            if ($device['os_group'] === 'arista') {
1105
                $bgp_ip = str_replace("$afi.", '', $bgp_ip);
1106
            }
1107
        }
1108
        $bgp_ip = preg_replace('/:/', ' ', $bgp_ip);
1109
        $bgp_ip = preg_replace('/(\S+\s+\S+)\s/', '$1:', $bgp_ip);
1110
        $bgp_ip = str_replace('"', '', str_replace(' ', '', $bgp_ip));
1111
1112
        if ($afi && $safi && $bgp_ip == $peer['ip']) {
1113
            $af_list[$bgp_ip][$afi][$safi] = 1;
1114
            add_cbgp_peer($device, $peer, $afi, $safi);
1115
        }
1116
    }
1117
1118
    return $af_list;
1119
}
1120
1121
function add_bgp_peer($device, $peer)
1122
{
1123
    if (dbFetchCell('SELECT COUNT(*) from `bgpPeers` WHERE device_id = ? AND bgpPeerIdentifier = ?', [$device['device_id'], $peer['ip']]) < '1') {
1124
        $bgpPeers = [
1125
            'device_id' => $device['device_id'],
1126
            'bgpPeerIdentifier' => $peer['ip'],
1127
            'bgpPeerRemoteAs' => $peer['as'],
1128
            'context_name' => $device['context_name'],
1129
            'astext' => $peer['astext'],
1130
            'bgpPeerState' => 'idle',
1131
            'bgpPeerAdminStatus' => 'stop',
1132
            'bgpLocalAddr' => $peer['localip'] ?: '0.0.0.0',
1133
            'bgpPeerRemoteAddr' => '0.0.0.0',
1134
            'bgpPeerInUpdates' => 0,
1135
            'bgpPeerOutUpdates' => 0,
1136
            'bgpPeerInTotalMessages' => 0,
1137
            'bgpPeerOutTotalMessages' => 0,
1138
            'bgpPeerFsmEstablishedTime' => 0,
1139
            'bgpPeerInUpdateElapsedTime' => 0,
1140
        ];
1141
        dbInsert($bgpPeers, 'bgpPeers');
1142
        if (Config::get('autodiscovery.bgp')) {
1143
            $name = gethostbyaddr($peer['ip']);
1144
            discover_new_device($name, $device, 'BGP');
1145
        }
1146
        echo '+';
1147
    } else {
1148
        dbUpdate(['bgpPeerRemoteAs' => $peer['as'], 'astext' => $peer['astext']], 'bgpPeers', 'device_id=? AND bgpPeerIdentifier=?', [$device['device_id'], $peer['ip']]);
1149
        echo '.';
1150
    }
1151
}
1152
1153
function add_cbgp_peer($device, $peer, $afi, $safi)
1154
{
1155
    if (dbFetchCell('SELECT COUNT(*) from `bgpPeers_cbgp` WHERE device_id = ? AND bgpPeerIdentifier = ? AND afi=? AND safi=?', [$device['device_id'], $peer['ip'], $afi, $safi]) == 0) {
1156
        $cbgp = [
1157
            'device_id' => $device['device_id'],
1158
            'bgpPeerIdentifier' => $peer['ip'],
1159
            'afi' => $afi,
1160
            'safi' => $safi,
1161
            'context_name' => $device['context_name'],
1162
            'AcceptedPrefixes' => 0,
1163
            'DeniedPrefixes' => 0,
1164
            'PrefixAdminLimit' => 0,
1165
            'PrefixThreshold' => 0,
1166
            'PrefixClearThreshold' => 0,
1167
            'AdvertisedPrefixes' => 0,
1168
            'SuppressedPrefixes' => 0,
1169
            'WithdrawnPrefixes' => 0,
1170
            'AcceptedPrefixes_delta' => 0,
1171
            'AcceptedPrefixes_prev' => 0,
1172
            'DeniedPrefixes_delta' => 0,
1173
            'DeniedPrefixes_prev' => 0,
1174
            'AdvertisedPrefixes_delta' => 0,
1175
            'AdvertisedPrefixes_prev' => 0,
1176
            'SuppressedPrefixes_delta' => 0,
1177
            'SuppressedPrefixes_prev' => 0,
1178
            'WithdrawnPrefixes_delta' => 0,
1179
            'WithdrawnPrefixes_prev' => 0,
1180
        ];
1181
        dbInsert($cbgp, 'bgpPeers_cbgp');
1182
    }
1183
}
1184
1185
/**
1186
 * check if we should skip this sensor from discovery
1187
 *
1188
 * @param $device
1189
 * @param  string  $sensor_class
1190
 * @param  string  $sensor_descr
1191
 * @return bool
1192
 */
1193
function can_skip_sensor($device, $sensor_class = '', $sensor_descr = '')
1194
{
1195
    if (! empty($sensor_class) && Config::getCombined($device['os'], "disabled_sensors.$sensor_class", false)) {
1196
        return true;
1197
    }
1198
    foreach (Config::getCombined($device['os'], 'disabled_sensors_regex', []) as $skipRegex) {
1199
        if (! empty($sensor_descr) && preg_match($skipRegex, $sensor_descr)) {
1200
            return true;
1201
        }
1202
    }
1203
    foreach (Config::getCombined($device['os'], "disabled_sensors_regex.$sensor_class", []) as $skipRegex) {
1204
        if (! empty($sensor_descr) && preg_match($skipRegex, $sensor_descr)) {
1205
            return true;
1206
        }
1207
    }
1208
1209
    return false;
1210
}
1211
1212
/**
1213
 * check if we should skip this device from discovery
1214
 *
1215
 * @param  string  $sysName
1216
 * @param  string  $sysDescr
1217
 * @param  string  $platform
1218
 * @return bool
1219
 */
1220
function can_skip_discovery($sysName, $sysDescr = '', $platform = '')
1221
{
1222
    if ($sysName) {
1223
        foreach ((array) Config::get('autodiscovery.xdp_exclude.sysname_regexp') as $needle) {
1224
            if (preg_match($needle . 'i', $sysName)) {
1225
                d_echo("$sysName - regexp '$needle' matches '$sysName' - skipping device discovery \n");
1226
1227
                return true;
1228
            }
1229
        }
1230
    }
1231
1232
    if ($sysDescr) {
1233
        foreach ((array) Config::get('autodiscovery.xdp_exclude.sysdesc_regexp') as $needle) {
1234
            if (preg_match($needle . 'i', $sysDescr)) {
1235
                d_echo("$sysName - regexp '$needle' matches '$sysDescr' - skipping device discovery \n");
1236
1237
                return true;
1238
            }
1239
        }
1240
    }
1241
1242
    if ($platform) {
1243
        foreach ((array) Config::get('autodiscovery.cdp_exclude.platform_regexp') as $needle) {
1244
            if (preg_match($needle . 'i', $platform)) {
1245
                d_echo("$sysName - regexp '$needle' matches '$platform' - skipping device discovery \n");
1246
1247
                return true;
1248
            }
1249
        }
1250
    }
1251
1252
    return false;
1253
}
1254
1255
/**
1256
 * Try to find a device by sysName, hostname, ip, or mac_address
1257
 * If a device cannot be found, returns 0
1258
 *
1259
 * @param  string  $name  sysName or hostname
1260
 * @param  string  $ip  May be an IP or hex string
1261
 * @param  string  $mac_address
1262
 * @return int the device_id or 0
1263
 */
1264
function find_device_id($name = '', $ip = '', $mac_address = '')
1265
{
1266
    $where = [];
1267
    $params = [];
1268
1269
    if ($name && \LibreNMS\Util\Validate::hostname($name)) {
1270
        $where[] = '`hostname`=?';
1271
        $params[] = $name;
1272
1273
        if ($mydomain = Config::get('mydomain')) {
1274
            $where[] = '`hostname`=?';
1275
            $params[] = "$name.$mydomain";
1276
1277
            $where[] = 'concat(`hostname`, \'.\', ?) =?';
1278
            $params[] = "$mydomain";
1279
            $params[] = "$name";
1280
        }
1281
    }
1282
1283
    if ($ip) {
1284
        $where[] = '`hostname`=?';
1285
        $params[] = $ip;
1286
1287
        try {
1288
            $params[] = IP::fromHexString($ip)->packed();
1289
            $where[] = '`ip`=?';
1290
        } catch (InvalidIpException $e) {
1291
            //
1292
        }
1293
    }
1294
1295
    if (! empty($where)) {
1296
        $sql = 'SELECT `device_id` FROM `devices` WHERE ' . implode(' OR ', $where);
1297
        if ($device_id = dbFetchCell($sql, $params)) {
1298
            return (int) $device_id;
1299
        }
1300
    }
1301
1302
    if ($mac_address && $mac_address != '000000000000') {
1303
        if ($device_id = dbFetchCell('SELECT `device_id` FROM `ports` WHERE `ifPhysAddress`=?', [$mac_address])) {
1304
            return (int) $device_id;
1305
        }
1306
    }
1307
1308
    if ($name) {
1309
        $where = [];
1310
        $params = [];
1311
1312
        $where[] = '`sysName`=?';
1313
        $params[] = $name;
1314
1315
        if ($mydomain = Config::get('mydomain')) {
1316
            $where[] = '`sysName`=?';
1317
            $params[] = "$name.$mydomain";
1318
1319
            $where[] = 'concat(`sysName`, \'.\', ?) =?';
1320
            $params[] = "$mydomain";
1321
            $params[] = "$name";
1322
        }
1323
1324
        $sql = 'SELECT `device_id` FROM `devices` WHERE ' . implode(' OR ', $where) . ' LIMIT 2';
1325
        $ids = dbFetchColumn($sql, $params);
1326
        if (count($ids) == 1) {
1327
            return (int) $ids[0];
1328
        } elseif (count($ids) > 1) {
1329
            d_echo("find_device_id: more than one device found with sysName '$name'.\n");
1330
            // don't do anything, try other methods, if any
1331
        }
1332
    }
1333
1334
    return 0;
1335
}
1336
1337
/**
1338
 * Try to find a port by ifDescr, ifName, ifAlias, or MAC
1339
 *
1340
 * @param  string  $description  matched against ifDescr, ifName, and ifAlias
1341
 * @param  string  $identifier  matched against ifDescr, ifName, and ifAlias
1342
 * @param  int  $device_id  restrict search to ports on a specific device
1343
 * @param  string  $mac_address  check against ifPhysAddress (should be in lowercase hexadecimal)
1344
 * @return int
1345
 */
1346
function find_port_id($description, $identifier = '', $device_id = 0, $mac_address = null)
1347
{
1348
    if (! ($device_id || $mac_address)) {
1349
        return 0;
1350
    }
1351
1352
    $statements = [];
1353
    $params = [];
1354
1355
    if ($device_id) {
1356
        if ($description) {
1357
            // order is important here, the standard says this is ifDescr, which some mfg confuse with ifName
1358
            $statements[] = 'SELECT `port_id` FROM `ports` WHERE `device_id`=? AND (`ifDescr`=? OR `ifName`=?)';
1359
            $params[] = $device_id;
1360
            $params[] = $description;
1361
            $params[] = $description;
1362
        }
1363
1364
        if ($identifier) {
1365
            if (is_numeric($identifier)) {
1366
                $statements[] = 'SELECT `port_id` FROM `ports` WHERE `device_id`=? AND (`ifIndex`=? OR `ifAlias`=?)';
1367
            } else {
1368
                $statements[] = 'SELECT `port_id` FROM `ports` WHERE `device_id`=? AND (`ifDescr`=? OR `ifName`=?)';
1369
            }
1370
            $params[] = $device_id;
1371
            $params[] = $identifier;
1372
            $params[] = $identifier;
1373
        }
1374
1375
        if ($description) {
1376
            // we check ifAlias last because this is a user editable field, but some bad LLDP implementations use it
1377
            $statements[] = 'SELECT `port_id` FROM `ports` WHERE `device_id`=? AND `ifAlias`=?';
1378
            $params[] = $device_id;
1379
            $params[] = $description;
1380
        }
1381
    }
1382
1383
    if ($mac_address) {
1384
        $mac_statement = 'SELECT `port_id` FROM `ports` WHERE ';
1385
        if ($device_id) {
1386
            $mac_statement .= '`device_id`=? AND ';
1387
            $params[] = $device_id;
1388
        }
1389
        $mac_statement .= '`ifPhysAddress`=?';
1390
1391
        $statements[] = $mac_statement;
1392
        $params[] = $mac_address;
1393
    }
1394
1395
    if (empty($statements)) {
1396
        return 0;
1397
    }
1398
1399
    $queries = implode(' UNION ', $statements);
1400
    $sql = "SELECT * FROM ($queries LIMIT 1) p";
1401
1402
    return (int) dbFetchCell($sql, $params);
1403
}
1404