%PDF- %PDF-
| Direktori : /proc/self/root/backups/router/usr/local/etc/inc/plugins.inc.d/ |
| Current File : //proc/self/root/backups/router/usr/local/etc/inc/plugins.inc.d/dpinger.inc |
<?php
/*
* Copyright (C) 2020 Deciso B.V.
* Copyright (C) 2018 Martin Wasley <martin@team-rebellion.net>
* Copyright (C) 2016-2023 Franco Fichtner <franco@opnsense.org>
* Copyright (C) 2008 Bill Marquette <bill.marquette@gmail.com>
* Copyright (C) 2008 Seth Mos <seth.mos@dds.nl>
* Copyright (C) 2010 Ermal Luçi
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
function dpinger_services()
{
$services = [];
foreach (dpinger_instances() as $name => $gateway) {
$pconfig = [];
$pconfig['description'] = sprintf(gettext('Gateway monitor (%s)'), $gateway['name']);
$pconfig['php']['restart'] = ['dpinger_configure_do'];
$pconfig['php']['start'] = ['dpinger_configure_do'];
$pconfig['pidfile'] = "/var/run/dpinger_{$gateway['name']}.pid";
$pconfig['php']['args'] = ['verbose', 'id'];
$pconfig['name'] = 'dpinger';
$pconfig['verbose'] = false;
$pconfig['id'] = $gateway['name'];
$services[] = $pconfig;
}
if (count($services)) {
$pconfig = [];
$pconfig['description'] = gettext('Gateway monitor watcher');
$pconfig['php']['restart'] = ['dpinger_configure_do'];
$pconfig['php']['start'] = ['dpinger_configure_do'];
$pconfig['pidfile'] = '/var/run/gateway_watcher.pid';
$pconfig['php']['args'] = ['verbose', 'id'];
$pconfig['name'] = 'dpinger';
$pconfig['verbose'] = false;
$pconfig['id'] = ':watcher:';
$pconfig['locked'] = true;
/* add as first entry which is used as "global" control */
array_unshift($services, $pconfig);
}
return $services;
}
function dpinger_configure()
{
return [
'monitor' => ['dpinger_configure_do:2'],
];
}
function dpinger_syslog()
{
$logfacilities = [];
$logfacilities['gateways'] = ['facility' => ['dpinger']];
return $logfacilities;
}
function dpinger_host_routes()
{
$routes = [];
foreach (dpinger_instances() as $gateway) {
if (!empty($gateway['monitor_noroute'])) {
/* no need to register */
continue;
} elseif (empty($gateway['gateway'])) {
/* previously reported as empty */
continue;
} elseif (isset($routes[$gateway['monitor']])) {
log_msg("Duplicated monitor route ignored for {$gateway['monitor']} on {$gateway['interface']}", LOG_WARNING);
continue;
}
$routes[$gateway['monitor']] = $gateway['gateway'];
}
return $routes;
}
function dpinger_instances($extended = false)
{
$instances = [];
foreach ((new \OPNsense\Routing\Gateways())->gatewaysIndexedByName() as $name => $gateway) {
if (!empty($gateway['monitor_disable']) && !$extended) {
/* do not monitor if such was requested */
continue;
}
foreach (['monitor', 'gateway'] as $key) {
/* add link-local scope where missing */
if (!empty($gateway[$key]) && is_linklocal($gateway[$key]) && strpos($gateway[$key], '%') === false) {
$gateway[$key] .= '%' . $gateway['if'];
}
}
$instances[$name] = $gateway;
}
return $instances;
}
function dpinger_configure_do($verbose = false, $gwname_map = null)
{
if (!plugins_argument_map($gwname_map)) {
return;
}
service_log(sprintf('Setting up gateway monitor%s...', empty($gwname_map) ? '' : ' for ' . join(', ', $gwname_map)), $verbose);
foreach (dpinger_processes() as $running_gwname => $proc) {
if (!empty($gwname_map) && !in_array($running_gwname, $gwname_map)) {
continue;
}
if (isvalidpid($proc['pidfile'])) {
killbypid($proc['pidfile']);
}
}
if (!empty($gwname_map) && in_array(':watcher:', $gwname_map)) {
/* allow the watcher to be restarted as well */
killbypid('/var/run/gateway_watcher.pid');
}
$ifconfig_details = legacy_interfaces_details();
$routes = [];
foreach (dpinger_instances() as $name => $gateway) {
if (!empty($gwname_map) && !in_array($name, $gwname_map)) {
continue;
}
foreach (['monitor', 'gateway'] as $key) {
if (empty($gateway[$key])) {
log_msg("Skipping gateway {$name} due to empty '{$key}' property.", LOG_WARNING);
continue;
}
}
$gwifip = null;
if ($gateway['ipprotocol'] == 'inet') {
if (is_ipaddrv4($gateway['gateway'])) {
foreach (interfaces_addresses($gateway['interface'], false, $ifconfig_details) as $addr) {
/* explicitly do not require $addr['alias'] to be true here */
if ($addr['family'] != 'inet') {
continue;
}
$network = gen_subnet($addr['address'], $addr['bits']) . "/{$addr['bits']}";
if (ip_in_subnet($gateway['gateway'], $network)) {
$gwifip = $addr['address'];
break;
}
}
}
if (empty($gwifip)) {
list ($gwifip) = interfaces_primary_address($gateway['interface'], $ifconfig_details);
if (!empty($gwifip) && is_ipaddrv4($gateway['gateway'])) {
log_msg(sprintf('Chose to bind %s on %s since we could not find a proper match.', $name, $gwifip));
}
}
if (empty($gwifip)) {
log_msg(sprintf('The required %s IPv4 interface address could not be found, skipping.', $name), LOG_WARNING);
continue;
}
} elseif ($gateway['ipprotocol'] == 'inet6') {
if (is_linklocal($gateway['monitor'])) {
/* link local monitor needs a link local address for the "src" part */
list ($gwifip) = interfaces_scoped_address6($gateway['interface'], $ifconfig_details);
} else {
list ($gwifip) = interfaces_routed_address6($gateway['interface'], $ifconfig_details);
}
if (empty($gwifip) && is_ipaddrv6($gateway['gateway'])) {
foreach (interfaces_addresses($gateway['interface'], false, $ifconfig_details) as $addr) {
if ($addr['family'] != 'inet6' || !$addr['alias']) {
continue;
}
$networkv6 = gen_subnetv6($addr['address'], $addr['bits']) . "/{$addr['bits']}";
if (ip_in_subnet($gateway['gateway'], $networkv6)) {
$gwifip = $addr['address'];
break;
}
}
}
if (empty($gwifip)) {
log_msg(sprintf('The required %s IPv6 interface address could not be found, skipping.', $name), LOG_WARNING);
continue;
}
} else {
log_msg(sprintf('Unknown address family "%s" during monitor setup', $gateway['ipprotocol']), LOG_ERR);
continue;
}
if (!empty($gateway['gateway']) && empty($gateway['monitor_noroute'])) {
if (isset($routes[$gateway['monitor']])) {
log_msg("Duplicated monitor route ignored for {$gateway['monitor']} on {$gateway['interface']}", LOG_WARNING);
} else {
system_host_route($gateway['monitor'], $gateway['gateway']);
$routes[$gateway['monitor']] = $gateway['gateway'];
}
}
/* log warnings via syslog */
$params = '-S ';
/* disable unused reporting thread */
$params .= '-r 0 ';
/* identifier */
$params .= exec_safe('-i %s ', $name);
/* bind src address */
$params .= exec_safe('-B %s ', $gwifip);
/* PID filename */
$params .= exec_safe('-p %s ', "/var/run/dpinger_{$name}.pid");
/* status socket */
$params .= exec_safe('-u %s ', "/var/run/dpinger_{$name}.sock");
foreach (
[
'interval' => '-s %ss ',
'loss_interval' => '-l %ss ',
'time_period' => '-t %ss ',
'data_length' => '-d %s '
] as $pname => $ppattern
) {
$params .= exec_safe($ppattern, $gateway["current_" . $pname]);
}
$params .= exec_safe('%s ', $gateway['monitor']);
/* foreground mode in background to deal with tentative connectivity */
mwexec_bg("/usr/local/bin/dpinger -f {$params}");
}
if (count(dpinger_services())) {
if (isvalidpid('/var/run/gateway_watcher.pid')) {
/* indicate that the configuration needs a reload */
killbypid('/var/run/gateway_watcher.pid', 'HUP');
} else {
/* use a separate script to produce the monitor alerts which runs forever */
mwexecf(
'/usr/sbin/daemon -f -p %s /usr/local/opnsense/scripts/routes/gateway_watcher.php %s',
['/var/run/gateway_watcher.pid', 'interface routes alarm']
);
}
} else {
killbypid('/var/run/gateway_watcher.pid');
}
service_log("done.\n", $verbose);
}
function dpinger_run()
{
return [
'host_routes' => 'dpinger_host_routes',
'return_gateways_status' => 'dpinger_status',
];
}
function dpinger_status()
{
$procs = dpinger_processes();
$status = [];
foreach (dpinger_instances(true) as $gwitem) {
/* we seem to be concerned about disabled monitors just because of force_down */
$gwstatus = !empty($gwitem['monitor_disable']) ? 'none' : 'down';
$gwname = $gwitem['name'];
$status[$gwname] = $report = [
'status' => !empty($gwitem['force_down']) ? 'force_down' : $gwstatus,
/* grab the runtime monitor from the instance if available */
'monitor' => !empty($gwitem['monitor']) ? $gwitem['monitor'] : '~',
'name' => $gwname,
'stddev' => '~',
'delay' => '~',
'loss' => '~',
];
if (!isset($procs[$gwitem['name']])) {
continue;
}
$fp = @stream_socket_client("unix://{$procs[$gwname]['socket']}", $errno, $errstr, 1);
if (!$fp) {
continue;
}
$dinfo = '';
while (!feof($fp)) {
$dinfo .= fgets($fp, 1024);
}
fclose($fp);
list(, $latency_avg, $latency_stddev, $loss) = explode(' ', preg_replace('/\n/', '', $dinfo));
if ($latency_stddev == '0' && $loss == '0') {
continue;
}
$latency_stddev = round($latency_stddev / 1000, 1);
$latency_avg = round($latency_avg / 1000, 1);
if (isset($gwitem['current_losshigh']) && $report['status'] != 'force_down') {
if ($latency_avg > $gwitem['current_latencyhigh'] || $loss > $gwitem['current_losshigh']) {
$report['status'] = 'down';
} elseif ($latency_avg > $gwitem['current_latencylow'] && $loss > $gwitem['current_losslow']) {
$report['status'] = 'delay+loss';
} elseif ($latency_avg > $gwitem['current_latencylow']) {
$report['status'] = 'delay';
} elseif ($loss > $gwitem['current_losslow']) {
$report['status'] = 'loss';
} else {
$report['status'] = 'none';
}
}
$report['delay'] = sprintf('%0.1f ms', empty($latency_avg) ? 0.0 : round($latency_avg, 1));
$report['stddev'] = sprintf('%0.1f ms', empty($latency_stddev) ? 0.0 : round($latency_stddev, 1));
$report['loss'] = sprintf('%0.1f %%', empty($loss) ? 0.0 : round($loss, 1));
/* rewrite report using gathered information */
$status[$gwname] = $report;
}
return $status;
}
function dpinger_processes()
{
$result = [];
$pidfiles = glob('/var/run/dpinger_*.pid');
if ($pidfiles === false) {
return $result;
}
foreach ($pidfiles as $pidfile) {
if (preg_match('/^dpinger_(.+)\.pid$/', basename($pidfile), $matches)) {
$socket_file = preg_replace('/\.pid$/', '.sock', $pidfile);
$result[$matches[1]] = [
'socket' => $socket_file,
'pidfile' => $pidfile,
];
}
}
return $result;
}