From 9e42d7fea694365d07e8e9c91636f2f619544f5b Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 00:58:50 +0100 Subject: [PATCH 01/93] Simplify code for entrypoint --- pfsense_zbx.php | 118 ++++++++++++++++++++---------------------------- 1 file changed, 49 insertions(+), 69 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index a45d4ea..6b5f354 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1274,73 +1274,53 @@ function pfz_discovery($section){ } } -//Main Code -switch (strtolower($argv[1])){ - case "discovery": - pfz_discovery($argv[2]); - break; - case "gw_value": - pfz_gw_value($argv[2],$argv[3]); - break; - case "gw_status": - pfz_gw_rawstatus(); - break; - case "if_speedtest_value": - pfz_speedtest_cron_install(); - pfz_interface_speedtest_value($argv[2],$argv[3]); - break; - case "openvpn_servervalue": - pfz_openvpn_servervalue($argv[2],$argv[3]); - break; - case "openvpn_server_uservalue": - pfz_openvpn_server_uservalue($argv[2],$argv[3]); - break; - case "openvpn_server_uservalue_numeric": - pfz_openvpn_server_uservalue($argv[2],$argv[3],"0"); - break; - case "openvpn_clientvalue": - pfz_openvpn_clientvalue($argv[2],$argv[3]); - break; - case "service_value": - pfz_service_value($argv[2],$argv[3]); - break; - case "carp_status": - pfz_carp_status(); - break; - case "if_name": - pfz_get_if_name($argv[2]); - break; - case "system": - pfz_get_system_value($argv[2]); - break; - case "ipsec_ph1": - pfz_ipsec_ph1($argv[2],$argv[3]); - break; - case "ipsec_ph2": - pfz_ipsec_ph2($argv[2],$argv[3]); - break; - case "dhcp": - pfz_dhcp($argv[2],$argv[3]); - break; - case "file_exists": - pfz_file_exists($argv[2]); - break; - case "speedtest_cron": - pfz_speedtest_cron_install(); - pfz_speedtest_cron(); - break; - case "cron_cleanup": - pfz_speedtest_cron_install(false); - break; - case "smart_status": - pfz_get_smart_status(); - break; - case "cert_date": - pfz_get_cert_date($argv[2]); - break; - case "temperature": - pfz_get_temperature($argv[2]); - break; - default: - pfz_test(); +$exec_0 = fn(callable $f) => $f; +$exec_1 = fn(callable $f) => fn($parameters) => $f($parameters[0]); +$exec_2 = fn(callable $f) => fn($parameters) => $f($parameters[0], $parameters[1]); + +define('COMMAND_HANDLERS', [ + "carp_status" => $exec_0(fn() => pfz_carp_status()), + "cert_date" => $exec_1(fn($p0) => pfz_get_cert_date($p0)), + "cron_cleanup" => $exec_0(fn() => pfz_speedtest_cron_install(false)), + "dhcp" => $exec_2(fn($p0, $p1) => pfz_dhcp($p0, $p1)), + "discovery" => $exec_1(fn($p0) => pfz_discovery($p0)), + "file_exists" => $exec_1(fn($p0) => pfz_file_exists($p0)), + "gw_status" => $exec_0(fn() => pfz_gw_rawstatus()), + "gw_value" => $exec_2(fn($p0, $p1) => pfz_gw_value($p0, $p1)), + "if_name" => $exec_1(fn($p0) => pfz_get_if_name($p0)), + "if_speedtest_value" => $exec_2(function ($p0, $p1) { + pfz_speedtest_cron_install(); + pfz_interface_speedtest_value($p0, $p1); + }), + "ipsec_ph1" => $exec_2(fn($p0, $p1) => pfz_ipsec_ph1($p0, $p1)), + "ipsec_ph2" => $exec_2(fn($p0, $p1) => pfz_ipsec_ph2($p0, $p1)), + "openvpn_clientvalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_clientvalue($p0, $p1)), + "openvpn_server_uservalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_server_uservalue($p0, $p1)), + "openvpn_server_uservalue_numeric" => $exec_2(fn($p0, $p1) => pfz_openvpn_server_uservalue($p0, $p1, "0")), + "openvpn_servervalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_servervalue($p0, $p1)), + "service_value" => $exec_2(fn($p0, $p1) => pfz_service_value($p0, $p1)), + "speedtest_cron" => $exec_0(function () { + pfz_speedtest_cron_install(); + pfz_speedtest_cron(); + }), + "smart_status" => $exec_0(fn() => pfz_get_smart_status()), + "system" => $exec_1(fn($p0) => pfz_get_system_value($p0)), + "temperature" => $exec_1(fn($p0) => pfz_get_temperature($p0)), +]); + +function main($arguments) +{ + $command = strtolower($arguments[1]); + $parameters = array_slice($arguments, 2); + + $is_known_command = array_key_exists($command, COMMAND_HANDLERS); + + if (!$is_known_command) { + pfz_test(); + return; + } + + COMMAND_HANDLERS[$command]($parameters); } + +main($argv); From c03733370ab6e36cc3585c629518d676983a532a Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Wed, 16 Feb 2022 13:22:30 +0100 Subject: [PATCH 02/93] Move command handlers to top of file --- pfsense_zbx.php | 67 +++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 6b5f354..e940ca5 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -8,9 +8,40 @@ This program is licensed under Apache 2.0 License */ //Some Useful defines - define('SPEEDTEST_INTERVAL', 8); //Speedtest Interval (in hours) +$exec_0 = fn(callable $f) => $f; +$exec_1 = fn(callable $f) => fn($parameters) => $f($parameters[0]); +$exec_2 = fn(callable $f) => fn($parameters) => $f($parameters[0], $parameters[1]); +define('COMMAND_HANDLERS', [ + "carp_status" => $exec_0(fn() => pfz_carp_status()), + "cert_date" => $exec_1(fn($p0) => pfz_get_cert_date($p0)), + "cron_cleanup" => $exec_0(fn() => pfz_speedtest_cron_install(false)), + "dhcp" => $exec_2(fn($p0, $p1) => pfz_dhcp($p0, $p1)), + "discovery" => $exec_1(fn($p0) => pfz_discovery($p0)), + "file_exists" => $exec_1(fn($p0) => pfz_file_exists($p0)), + "gw_status" => $exec_0(fn() => pfz_gw_rawstatus()), + "gw_value" => $exec_2(fn($p0, $p1) => pfz_gw_value($p0, $p1)), + "if_name" => $exec_1(fn($p0) => pfz_get_if_name($p0)), + "if_speedtest_value" => $exec_2(function ($p0, $p1) { + pfz_speedtest_cron_install(); + pfz_interface_speedtest_value($p0, $p1); + }), + "ipsec_ph1" => $exec_2(fn($p0, $p1) => pfz_ipsec_ph1($p0, $p1)), + "ipsec_ph2" => $exec_2(fn($p0, $p1) => pfz_ipsec_ph2($p0, $p1)), + "openvpn_clientvalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_clientvalue($p0, $p1)), + "openvpn_server_uservalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_server_uservalue($p0, $p1)), + "openvpn_server_uservalue_numeric" => $exec_2(fn($p0, $p1) => pfz_openvpn_server_uservalue($p0, $p1, "0")), + "openvpn_servervalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_servervalue($p0, $p1)), + "service_value" => $exec_2(fn($p0, $p1) => pfz_service_value($p0, $p1)), + "speedtest_cron" => $exec_0(function () { + pfz_speedtest_cron_install(); + pfz_speedtest_cron(); + }), + "smart_status" => $exec_0(fn() => pfz_get_smart_status()), + "system" => $exec_1(fn($p0) => pfz_get_system_value($p0)), + "temperature" => $exec_1(fn($p0) => pfz_get_temperature($p0)), +]); require_once('globals.inc'); require_once('functions.inc'); require_once('config.inc'); @@ -1274,40 +1305,6 @@ function pfz_discovery($section){ } } -$exec_0 = fn(callable $f) => $f; -$exec_1 = fn(callable $f) => fn($parameters) => $f($parameters[0]); -$exec_2 = fn(callable $f) => fn($parameters) => $f($parameters[0], $parameters[1]); - -define('COMMAND_HANDLERS', [ - "carp_status" => $exec_0(fn() => pfz_carp_status()), - "cert_date" => $exec_1(fn($p0) => pfz_get_cert_date($p0)), - "cron_cleanup" => $exec_0(fn() => pfz_speedtest_cron_install(false)), - "dhcp" => $exec_2(fn($p0, $p1) => pfz_dhcp($p0, $p1)), - "discovery" => $exec_1(fn($p0) => pfz_discovery($p0)), - "file_exists" => $exec_1(fn($p0) => pfz_file_exists($p0)), - "gw_status" => $exec_0(fn() => pfz_gw_rawstatus()), - "gw_value" => $exec_2(fn($p0, $p1) => pfz_gw_value($p0, $p1)), - "if_name" => $exec_1(fn($p0) => pfz_get_if_name($p0)), - "if_speedtest_value" => $exec_2(function ($p0, $p1) { - pfz_speedtest_cron_install(); - pfz_interface_speedtest_value($p0, $p1); - }), - "ipsec_ph1" => $exec_2(fn($p0, $p1) => pfz_ipsec_ph1($p0, $p1)), - "ipsec_ph2" => $exec_2(fn($p0, $p1) => pfz_ipsec_ph2($p0, $p1)), - "openvpn_clientvalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_clientvalue($p0, $p1)), - "openvpn_server_uservalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_server_uservalue($p0, $p1)), - "openvpn_server_uservalue_numeric" => $exec_2(fn($p0, $p1) => pfz_openvpn_server_uservalue($p0, $p1, "0")), - "openvpn_servervalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_servervalue($p0, $p1)), - "service_value" => $exec_2(fn($p0, $p1) => pfz_service_value($p0, $p1)), - "speedtest_cron" => $exec_0(function () { - pfz_speedtest_cron_install(); - pfz_speedtest_cron(); - }), - "smart_status" => $exec_0(fn() => pfz_get_smart_status()), - "system" => $exec_1(fn($p0) => pfz_get_system_value($p0)), - "temperature" => $exec_1(fn($p0) => pfz_get_temperature($p0)), -]); - function main($arguments) { $command = strtolower($arguments[1]); From 5e4a515b3a9e0f1091966a43a88abbb864744c5a Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Wed, 16 Feb 2022 13:30:03 +0100 Subject: [PATCH 03/93] Simplify discovery --- pfsense_zbx.php | 67 ++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 40 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index e940ca5..88f7b9a 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -7,12 +7,28 @@ Written by Riccardo Bicelli This program is licensed under Apache 2.0 License */ -//Some Useful defines -define('SPEEDTEST_INTERVAL', 8); //Speedtest Interval (in hours) - $exec_0 = fn(callable $f) => $f; $exec_1 = fn(callable $f) => fn($parameters) => $f($parameters[0]); $exec_2 = fn(callable $f) => fn($parameters) => $f($parameters[0], $parameters[1]); + +//Some Useful defines +define('SPEEDTEST_INTERVAL', 8); //Speedtest Interval (in hours) + +// Argument parsers for Discovery +define('DISCOVERY_SECTION_HANDLERS', [ + "gw" => $exec_0(fn() => pfz_gw_discovery()), + "wan" => $exec_0(fn() => pfz_interface_discovery(true)), + "openvpn_server" => $exec_0(fn() => pfz_openvpn_serverdiscovery()), + "openvpn_server_user" => $exec_0(fn() => pfz_openvpn_server_userdiscovery()), + "openvpn_client" => $exec_0(fn() => pfz_openvpn_clientdiscovery()), + "services" => $exec_0(fn() => pfz_services_discovery()), + "interfaces" => $exec_0(fn() => pfz_interface_discovery()), + "ipsec_ph1" => $exec_0(fn() => pfz_ipsec_discovery_ph1()), + "ipsec_ph2" => $exec_0(fn() => pfz_ipsec_discovery_ph2()), + "dhcpfailover" => $exec_0(fn() => pfz_dhcpfailover_discovery()), + "temperature_sensors" => $exec_0(fn() => pfz_temperature_sensors_discovery()), +]); + define('COMMAND_HANDLERS', [ "carp_status" => $exec_0(fn() => pfz_carp_status()), "cert_date" => $exec_1(fn($p0) => pfz_get_cert_date($p0)), @@ -1266,43 +1282,14 @@ function pfz_valuemap($valuename, $value, $default="0"){ return $default; } -//Argument parsers for Discovery -function pfz_discovery($section){ - switch (strtolower($section)){ - case "gw": - pfz_gw_discovery(); - break; - case "wan": - pfz_interface_discovery(true); - break; - case "openvpn_server": - pfz_openvpn_serverdiscovery(); - break; - case "openvpn_server_user": - pfz_openvpn_server_userdiscovery(); - break; - case "openvpn_client": - pfz_openvpn_clientdiscovery(); - break; - case "services": - pfz_services_discovery(); - break; - case "interfaces": - pfz_interface_discovery(); - break; - case "ipsec_ph1": - pfz_ipsec_discovery_ph1(); - break; - case "ipsec_ph2": - pfz_ipsec_discovery_ph2(); - break; - case "dhcpfailover": - pfz_dhcpfailover_discovery(); - break; - case "temperature_sensors": - pfz_temperature_sensors_discovery(); - break; - } +function pfz_discovery($section) +{ + $is_known_section = array_key_exists(strtolower($section), DISCOVERY_SECTION_HANDLERS); + if (!$is_known_section) { + return; + } + + DISCOVERY_SECTION_HANDLERS[$section](); } function main($arguments) From 63b169b483aaa539be8d728613131d841dd1b240 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Wed, 16 Feb 2022 13:51:17 +0100 Subject: [PATCH 04/93] Simplify code for value mapping --- pfsense_zbx.php | 166 +++++++++++++++++++++--------------------------- 1 file changed, 72 insertions(+), 94 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 88f7b9a..7893460 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -58,6 +58,58 @@ define('COMMAND_HANDLERS', [ "system" => $exec_1(fn($p0) => pfz_get_system_value($p0)), "temperature" => $exec_1(fn($p0) => pfz_get_temperature($p0)), ]); + +define("VALUE_MAPPINGS", [ + "openvpn.server.status" => [ + "down" => "0", + "up" => "1", + "none" => "2", + "reconnecting; ping-restart" => "3", + "waiting" => "4", + "server_user_listening" => "5"], + "openvpn.client.status" => [ + "up" => "1", + "down" => "0", + "none" => "0", + "reconnecting; ping-restart" => "2"], + "openvpn.server.mode" => [ + "p2p_tls" => "1", + "p2p_shared_key" => "2", + "server_tls" => "3", + "server_user" => "4", + "server_tls_user" => "5"], + "gateway.status" => [ + "online" => "0", + "none" => "0", + "loss" => "1", + "highdelay" => "2", + "highloss" => "3", + "force_down" => "4", + "down" => "5"], + "ipsec.iketype" => [ + "auto" => 0, + "ikev1" => 1, + "ikev2" => 2], + "ipsec.mode" => [ + "main" => 0, + "aggressive" => 1], + "ipsec.protocol" => [ + "both" => 0, + "inet" => 1, + "inet6" => 2], + "ipsec_ph2.mode" => [ + "transport" => 0, + "tunnel" => 1, + "tunnel6" => 2], + "ipsec_ph2.protocol" => [ + "esp" => 1, + "ah" => 2], + "ipsec.state" => [ + "established" => 1, + "connecting" => 2, + "installed" => 1, + "rekeyed" => 2]]); + require_once('globals.inc'); require_once('functions.inc'); require_once('config.inc'); @@ -333,11 +385,11 @@ function pfz_openvpn_servervalue($server_id,$valuekey){ case "status": - $value = pfz_valuemap("openvpn.server.status", $value); + $value = pfz_value_mapping("openvpn.server.status", $value); break; case "mode": - $value = pfz_valuemap("openvpn.server.mode", $value); + $value = pfz_value_mapping("openvpn.server.mode", $value); break; } @@ -441,7 +493,7 @@ function pfz_openvpn_clientvalue($client_id, $valuekey, $default="none"){ switch ($valuekey){ case "status": - $value = pfz_valuemap("openvpn.client.status", $value); + $value = pfz_value_mapping("openvpn.client.status", $value); break; } @@ -581,7 +633,7 @@ function pfz_gw_value($gw, $valuekey) { if ($gws[$gw]["substatus"]<>"none") $value = $gws[$gw]["substatus"]; - $value = pfz_valuemap("gateway.status", $value); + $value = pfz_value_mapping("gateway.status", $value); } echo $value; } @@ -634,7 +686,7 @@ function pfz_ipsec_ph1($ikeid,$valuekey){ if ($valuekey=='disabled') $value = "1"; else - $value = pfz_valuemap("ipsec." . $valuekey, $data[$valuekey], $data[$valuekey]); + $value = pfz_value_mapping("ipsec." . $valuekey, $data[$valuekey], $data[$valuekey]); break; } } @@ -694,7 +746,7 @@ function pfz_ipsec_ph2($uniqid, $valuekey){ if ($valuekey=='disabled') $value = "1"; else - $value = pfz_valuemap("ipsec_ph2." . $valuekey, $data[$valuekey], $data[$valuekey]); + $value = pfz_value_mapping("ipsec_ph2." . $valuekey, $data[$valuekey], $data[$valuekey]); break; } } @@ -776,7 +828,7 @@ function pfz_ipsec_status($ikeid,$reqid=-1,$valuekey='state'){ switch($valuekey) { case 'state': - $value = pfz_valuemap('ipsec.state', strtolower($tmp_value)); + $value = pfz_value_mapping('ipsec.state', strtolower($tmp_value)); if ($carp_status!=0) $value = $value + (10 * ($carp_status-1)); break; default: @@ -1190,96 +1242,22 @@ function pfz_file_exists($filename) { // Value mappings // Each value map is represented by an associative array -function pfz_valuemap($valuename, $value, $default="0"){ - switch ($valuename){ +function pfz_value_mapping($value_name, $value, $default_value = "0") +{ + $is_known_value_name = array_key_exists($value_name, VALUE_MAPPINGS); + if (!$is_known_value_name) { + return $default_value; + } - case "openvpn.server.status": - $valuemap = array( - "down" => "0", - "up" => "1", - "none" => "2", - "reconnecting; ping-restart" => "3", - "waiting" => "4", - "server_user_listening" => "5"); - break; - - case "openvpn.client.status": - $valuemap = array( - "up" => "1", - "down" => "0", - "none" => "0", - "reconnecting; ping-restart" => "2"); - break; + $value_mapping = VALUE_MAPPINGS[$value_name]; + if (!is_array($value_mapping)) { + return $default_value; + } - case "openvpn.server.mode": - $valuemap = array( - "p2p_tls" => "1", - "p2p_shared_key" => "2", - "server_tls" => "3", - "server_user" => "4", - "server_tls_user" => "5"); - break; - - case "gateway.status": - $valuemap = array( - "online" => "0", - "none" => "0", - "loss" => "1", - "highdelay" => "2", - "highloss" => "3", - "force_down" => "4", - "down" => "5"); - break; - - case "ipsec.iketype": - $valuemap = array ( - "auto" => 0, - "ikev1" => 1, - "ikev2" => 2); - break; - - case "ipsec.mode": - $valuemap = array ( - "main" => 0, - "aggressive" => 1); - break; - - case "ipsec.protocol": - $valuemap = array ( - "both" => 0, - "inet" => 1, - "inet6" => 2); - break; - - case "ipsec_ph2.mode": - $valuemap = array ( - "transport" => 0, - "tunnel" => 1, - "tunnel6" => 2); - break; - - case "ipsec_ph2.protocol": - $valuemap = array ( - "esp" => 1, - "ah" => 2); - break; + $value = strtolower($value); + $is_value_with_known_mapping = array_key_exists($value, $value_mapping); - case "ipsec.state": - $valuemap = array ( - "established" => 1, - "connecting" => 2, - "installed" => 1, - "rekeyed" => 2); - break; - - } - - if (is_array($valuemap)) { - $value = strtolower($value); - if (array_key_exists($value, $valuemap)) - return $valuemap[$value]; - } - return $default; + return $is_value_with_known_mapping ? $value_mapping[$value] : $default_value; } function pfz_discovery($section) From 956323ec0e01d598abb0a574c1cb66ab0542ccc9 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Wed, 16 Feb 2022 14:54:56 +0100 Subject: [PATCH 05/93] Simplify SMART status retrieval --- pfsense_zbx.php | 68 +++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 7893460..d605b40 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -110,6 +110,20 @@ define("VALUE_MAPPINGS", [ "installed" => 1, "rekeyed" => 2]]); +const SMART_DEV_PASSED = "PASSED"; +const SMART_DEV_OK = "OK"; +const SMART_DEV_UNKNOWN = ""; + +const SMART_OK = 0; +const SMART_UNKNOWN = 2; +const SMART_ERROR = 1; + +define('SMART_DEV_STATUS', [ + SMART_DEV_PASSED => SMART_OK, + SMART_DEV_OK => SMART_OK, + SMART_DEV_UNKNOWN => SMART_UNKNOWN +]); + require_once('globals.inc'); require_once('functions.inc'); require_once('config.inc'); @@ -1164,10 +1178,7 @@ function pfz_get_system_value($section){ break; case "new_version_available": $pkgver = get_system_pkg_version(); - if ($pkgver['version']==$pkgver['installed_version']) - echo "0"; - else - echo "1"; + echo pfz_bint($pkgver['version']==$pkgver['installed_version']); break; case "packages_update": echo pfz_packages_uptodate(); @@ -1177,35 +1188,23 @@ function pfz_get_system_value($section){ //S.M.A.R.T Status // Taken from /usr/local/www/widgets/widgets/smart_status.widget.php -function pfz_get_smart_status(){ - - $devs = get_smart_drive_list(); - $status = 0; - foreach ($devs as $dev) { ## for each found drive do - $smartdrive_is_displayed = true; - $dev_ident = exec("diskinfo -v /dev/$dev | grep ident | awk '{print $1}'"); ## get identifier from drive - $dev_state = trim(exec("smartctl -H /dev/$dev | awk -F: '/^SMART overall-health self-assessment test result/ {print $2;exit} +function pfz_get_smart_status() +{ + foreach (get_smart_drive_list() as $dev) { ## for each found drive do + $dev_state = trim(exec("smartctl -H /dev/$dev | awk -F: '/^SMART overall-health self-assessment test result/ {print $2;exit} /^SMART Health Status/ {print $2;exit}'")); ## get SMART state from drive - switch ($dev_state) { - case "PASSED": - case "OK": - //OK - $status=0; - break; - case "": - //Unknown - $status=2; - return $status; - break; - default: - //Error - $status=1; - return $status; - break; - } - } - - echo $status; + $is_known_state = array_key_exists($dev_state, SMART_DEV_STATUS); + if (!$is_known_state) { + return SMART_ERROR; // ED This is probably a bug, status should be echoed + } + + $status = SMART_DEV_STATUS[$dev_state]; + if ($status !== SMART_OK) { + return $status; // ED This is probably a bug, status should be echoed + } + } + + echo SMART_OK; } // Certificats validity date @@ -1234,10 +1233,7 @@ function pfz_get_cert_date($valuekey){ // File is present function pfz_file_exists($filename) { - if (file_exists($filename)) - echo "1"; - else - echo "0"; + echo pfz_bint(file_exists($filename)); } // Value mappings From cb11d6851888523167e5ef3e26f3a8ee05e837eb Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Wed, 16 Feb 2022 15:04:55 +0100 Subject: [PATCH 06/93] Simplify system value retrieval --- pfsense_zbx.php | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index d605b40..781c2be 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1167,23 +1167,26 @@ function pfz_packages_uptodate(){ return $ret; } -//System Information -function pfz_get_system_value($section){ - switch ($section){ - case "version": - echo( get_system_pkg_version()['version']); - break; - case "installed_version": - echo( get_system_pkg_version()['installed_version']); - break; - case "new_version_available": - $pkgver = get_system_pkg_version(); - echo pfz_bint($pkgver['version']==$pkgver['installed_version']); - break; - case "packages_update": - echo pfz_packages_uptodate(); - break; - } +// System Information +function pfz_get_system_value($section) +{ + if ($section === "packages_update") { + echo pfz_packages_uptodate(); + return; + } + + $system_pkg_version = get_system_pkg_version(); + $version = $system_pkg_version["version"]; + $installed_version = $system_pkg_version["installed_version"]; + + if ($section === "new_version_available") { + echo pfz_bint($version, $installed_version); + return; + } + + if (array_key_exists($section, $system_pkg_version)) { + echo $system_pkg_version[$section]; + } } //S.M.A.R.T Status From 1229bf6370925fbb5aae90d8ecf8dfb9fc85b8e6 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Wed, 16 Feb 2022 15:10:09 +0100 Subject: [PATCH 07/93] Simplify package update status retrieval --- pfsense_zbx.php | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 781c2be..6cc2be6 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1151,20 +1151,15 @@ function pfz_dhcp($section, $valuekey=""){ } } -//Packages -function pfz_packages_uptodate(){ - require_once("pkg-utils.inc"); - $installed_packages = get_pkg_info('all', false, true); - - $ret = 0; +// Packages +function pfz_packages_uptodate() +{ + require_once("pkg-utils.inc"); + $installed_packages = get_pkg_info("all", false, true); - foreach ($installed_packages as $package){ - if ($package['version']!=$package['installed_version']){ - $ret ++; - } - } - - return $ret; + return count(array_filter( + $installed_packages, + fn($p) => $p["version"] != $p["installed_version"])); } // System Information From 798ffa8b8b61a274bedf230f6b1c3e331bb6b575 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Wed, 16 Feb 2022 15:15:56 +0100 Subject: [PATCH 08/93] Simplify dhcp --- pfsense_zbx.php | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 6cc2be6..331ff54 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -124,6 +124,12 @@ define('SMART_DEV_STATUS', [ SMART_DEV_UNKNOWN => SMART_UNKNOWN ]); +define("DHCP_SECTIONS", [ + "failover" => function () { + echo pfz_dhcp_check_failover(); + }, +]); + require_once('globals.inc'); require_once('functions.inc'); require_once('config.inc'); @@ -1128,27 +1134,24 @@ function pfz_dhcpfailover_discovery(){ echo $json_string; } -function pfz_dhcp_check_failover(){ - // Check DHCP Failover Status - // Returns number of failover pools which state is not normal or - // different than peer state - $failover = pfz_dhcp_get("failover"); - $ret = 0; - foreach ($failover as $f){ - if ( ($f["mystate"]!="normal") || ($f["mystate"]!=$f["peerstate"])) { - $ret++; - } - } - return $ret; +function pfz_dhcp_check_failover() +{ + // Check DHCP Failover Status + // Returns number of failover pools which state is not normal or + // different than peer state + $failover = pfz_dhcp_get("failover"); + + return count(array_filter($failover, fn($f) => ($f["mystate"] != "normal") || ($f["mystate"] != $f["peerstate"]))); } -function pfz_dhcp($section, $valuekey=""){ - switch ($section){ - case "failover": - echo pfz_dhcp_check_failover(); - break; - default: - } +function pfz_dhcp($section, $valuekey = "") +{ + $is_known_section = array_key_exists($section, DHCP_SECTIONS); + if (!$is_known_section) { + return; + } + + DHCP_SECTIONS[$section](); } // Packages From 958e4ca9e242c9e10c6e3ce8120c10fb4abbcba9 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Wed, 16 Feb 2022 15:56:02 +0100 Subject: [PATCH 09/93] Simplify OpenVPN server values --- pfsense_zbx.php | 96 +++++++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 39 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 331ff54..9eb3ed3 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -130,6 +130,13 @@ define("DHCP_SECTIONS", [ }, ]); +define("OPENVPN_SERVER_VALUES", [ + // Client Connections: is an array so it is sufficient to count elements + "conns" => fn($server_value) => is_array($server_value) ? count($server_value) : 0, + "status" => fn($server_value) => pfz_value_mapping("openvpn.server.status", $server_value), + "mode" => fn($server_value) => pfz_value_mapping("openvpn.server.mode", $server_value) +]); + require_once('globals.inc'); require_once('functions.inc'); require_once('config.inc'); @@ -149,6 +156,18 @@ require_once('pkg-utils.inc'); //For DHCP +// Utilities +function array_first(array $haystack, Callback $match) +{ + foreach ($haystack as $needle) { + if ($match($needle)) { + return $needle; + } + } + + return null; +} + //Testing function, for template creating purpose function pfz_test(){ $line = "-------------------\n"; @@ -373,48 +392,47 @@ function pfz_openvpn_serverdiscovery() { echo $json_string; } +function pfz_retrieve_server_value($maybe_server, $value_key) +{ + if (empty($maybe_server)) { + return null; + } + + $raw_value = $maybe_server[$value_key]; + + if (in_array($maybe_server["mode"], ["server_user", "server_tls_user", "server_tls"])) { + return $raw_value == "" ? "server_user_listening" : $raw_value; + } + + if ($maybe_server["mode"] == "p2p_tls") { + // For p2p_tls, ensure we have one client, and return up if it's the case + if ($raw_value == "") { + $has_at_least_one_connection = + is_array($maybe_server["conns"]) && count($maybe_server["conns"]) > 0; + + return $has_at_least_one_connection ? "up" : "down"; + } + } + + return $raw_value; +} // Get OpenVPN Server Value -function pfz_openvpn_servervalue($server_id,$valuekey){ - $servers = pfz_openvpn_get_all_servers(); - - foreach($servers as $server) { - if($server['vpnid']==$server_id){ - $value = $server[$valuekey]; - if ($valuekey=="status") { - if ( ($server['mode']=="server_user") || ($server['mode']=="server_tls_user") || ($server['mode']=="server_tls") ){ - if ($value=="") $value="server_user_listening"; - } else if ($server['mode']=="p2p_tls"){ - // For p2p_tls, ensure we have one client, and return up if it's the case - if ($value=="") - $value=(is_array($server["conns"]) && count($server["conns"]) > 0) ? "up" : "down"; - } - } - } - } - - switch ($valuekey){ - - case "conns": - //Client Connections: is an array so it is sufficient to count elements - if (is_array($value)) - $value = count($value); - else - $value = "0"; - break; - - case "status": - - $value = pfz_value_mapping("openvpn.server.status", $value); - break; +function pfz_openvpn_server_value($server_id, $value_key) +{ + $servers = pfz_openvpn_get_all_servers(); - case "mode": - $value = pfz_value_mapping("openvpn.server.mode", $value); - break; - } - - //if ($value=="") $value="none"; - echo $value; + $maybe_server = array_first($servers, fn($s) => $s['vpnid'] == $server_id); + + $server_value = pfz_retrieve_server_value($maybe_server, $value_key); + + $is_known_value_key = array_key_exists($value_key, OPENVPN_SERVER_VALUES); + if ($is_known_value_key) { + echo OPENVPN_SERVER_VALUES[$value_key]($server_value); + return; + } + + echo $server_value; } //OpenVPN Server/User-Auth Discovery From 10a51a990de28c99a69d881ef93ea51a00425099 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Wed, 16 Feb 2022 16:04:35 +0100 Subject: [PATCH 10/93] Simplify OpenVPN client value --- pfsense_zbx.php | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 9eb3ed3..77f9433 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -45,10 +45,10 @@ define('COMMAND_HANDLERS', [ }), "ipsec_ph1" => $exec_2(fn($p0, $p1) => pfz_ipsec_ph1($p0, $p1)), "ipsec_ph2" => $exec_2(fn($p0, $p1) => pfz_ipsec_ph2($p0, $p1)), - "openvpn_clientvalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_clientvalue($p0, $p1)), + "openvpn_clientvalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_client_value($p0, $p1)), "openvpn_server_uservalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_server_uservalue($p0, $p1)), "openvpn_server_uservalue_numeric" => $exec_2(fn($p0, $p1) => pfz_openvpn_server_uservalue($p0, $p1, "0")), - "openvpn_servervalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_servervalue($p0, $p1)), + "openvpn_servervalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_server_value($p0, $p1)), "service_value" => $exec_2(fn($p0, $p1) => pfz_service_value($p0, $p1)), "speedtest_cron" => $exec_0(function () { pfz_speedtest_cron_install(); @@ -521,26 +521,26 @@ function pfz_replacespecialchars($inputstr,$reverse=false){ return ($resultstr); } -function pfz_openvpn_clientvalue($client_id, $valuekey, $default="none"){ - $clients = openvpn_get_active_clients(); - foreach($clients as $client) { - if($client['vpnid']==$client_id) - $value = $client[$valuekey]; - } +function pfz_openvpn_client_value($client_id, $value_key, $fallback_value = "none") +{ + $clients = openvpn_get_active_clients(); - switch ($valuekey){ - - case "status": - $value = pfz_value_mapping("openvpn.client.status", $value); - break; + $client = array_first($clients, fn($client) => $client['vpnid'] == $client_id); - } + if (empty($client)) { + return $fallback_value; + } - if ($value=="") $value=$default; - echo $value; + $maybe_value = $client[$value_key]; + + $is_known_value_key = array_key_exists($value_key, OPENVPN_CLIENT_VALUE); + if ($is_known_value_key) { + return OPENVPN_CLIENT_VALUE[$value_key]($maybe_value); + } + + return ($maybe_value == "") ? $fallback_value : $maybe_value; } - // Services Discovery // 2020-03-27: Added space replace with __ for issue #12 function pfz_services_discovery(){ From 792990b7f69d889d128252f41fea055004d288b4 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Wed, 16 Feb 2022 16:20:36 +0100 Subject: [PATCH 11/93] Simplify ipsec ph1 --- pfsense_zbx.php | 83 ++++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 77f9433..b85994d 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -137,6 +137,11 @@ define("OPENVPN_SERVER_VALUES", [ "mode" => fn($server_value) => pfz_value_mapping("openvpn.server.mode", $server_value) ]); +define("IPSEC_PH1_VALUES", [ + 'status' => fn($ike_id) => pfz_ipsec_status($ike_id), + 'disabled' => fn() => "0", +]); + require_once('globals.inc'); require_once('functions.inc'); require_once('config.inc'); @@ -168,6 +173,12 @@ function array_first(array $haystack, Callback $match) return null; } +function pfz_cfg() { // Abstract global variable from code + global $config; + + return $config; +} + //Testing function, for template creating purpose function pfz_test(){ $line = "-------------------\n"; @@ -202,7 +213,7 @@ function pfz_test(){ echo "IPsec: \n"; require_once("ipsec.inc"); - global $config; + $config = pfz_cfg(); init_config_arr(array('ipsec', 'phase1')); init_config_arr(array('ipsec', 'phase2')); $a_phase2 = &$config['ipsec']['phase2']; @@ -682,7 +693,7 @@ function pfz_gw_value($gw, $valuekey) { function pfz_ipsec_discovery_ph1(){ require_once("ipsec.inc"); - global $config; + $config = pfz_cfg(); init_config_arr(array('ipsec', 'phase1')); $a_phase1 = &$config['ipsec']['phase1']; @@ -701,43 +712,45 @@ function pfz_ipsec_discovery_ph1(){ } -function pfz_ipsec_ph1($ikeid,$valuekey){ - // Get Value from IPsec Phase 1 Configuration - // If Getting "disabled" value only check item presence in config array +function pfz_ipsec_ph1($ike_id, $value_key) +{ + // Get Value from IPsec Phase 1 Configuration + // If Getting "disabled" value only check item presence in config array + require_once("ipsec.inc"); + $config = pfz_cfg(); + init_config_arr(array('ipsec', 'phase1')); + $a_phase1 = &$config['ipsec']['phase1']; - require_once("ipsec.inc"); - global $config; - init_config_arr(array('ipsec', 'phase1')); - $a_phase1 = &$config['ipsec']['phase1']; + $is_known_ipsec_key = array_key_exists($value_key, IPSEC_PH1_VALUES); + if ($is_known_ipsec_key) { + echo IPSEC_PH1_VALUES[$value_key]($ike_id); + return; + } - $value = ""; - switch ($valuekey) { - case 'status': - $value = pfz_ipsec_status($ikeid); - break; - case 'disabled': - $value = "0"; - default: - foreach ($a_phase1 as $data) { - if ($data['ikeid'] == $ikeid) { - if(array_key_exists($valuekey,$data)) { - if ($valuekey=='disabled') - $value = "1"; - else - $value = pfz_value_mapping("ipsec." . $valuekey, $data[$valuekey], $data[$valuekey]); - break; - } - } - } - } - echo $value; + $maybe_ike_match = array_first($a_phase1, fn($d) => $d["ikeid"] == $ike_id); + if (empty($maybe_ike_match)) { + echo ""; + return; + } + + if (!array_key_exists($value_key, $maybe_ike_match)) { + echo ""; + return; + } + + if ($value_key == 'disabled') { + echo "1"; + return; + } + + echo pfz_value_mapping("ipsec." . $value_key, $maybe_ike_match[$value_key]); } function pfz_ipsec_discovery_ph2(){ require_once("ipsec.inc"); - global $config; + $config = pfz_cfg(); init_config_arr(array('ipsec', 'phase2')); $a_phase2 = &$config['ipsec']['phase2']; @@ -761,7 +774,7 @@ function pfz_ipsec_discovery_ph2(){ function pfz_ipsec_ph2($uniqid, $valuekey){ require_once("ipsec.inc"); - global $config; + $config = pfz_cfg(); init_config_arr(array('ipsec', 'phase2')); $a_phase2 = &$config['ipsec']['phase2']; @@ -795,7 +808,7 @@ function pfz_ipsec_ph2($uniqid, $valuekey){ function pfz_ipsec_status($ikeid,$reqid=-1,$valuekey='state'){ require_once("ipsec.inc"); - global $config; + $config = pfz_cfg(); init_config_arr(array('ipsec', 'phase1')); $a_phase1 = &$config['ipsec']['phase1']; @@ -917,7 +930,7 @@ function pfz_get_temperature($sensorid){ function pfz_carp_status($echo = true){ //Detect CARP Status - global $config; + $config = pfz_cfg(); $status_return = 0; $status = get_carp_status(); $carp_detected_problems = get_single_sysctl("net.inet.carp.demotion"); @@ -1228,7 +1241,7 @@ function pfz_get_smart_status() // Certificats validity date function pfz_get_cert_date($valuekey){ - global $config; + $config = pfz_cfg(); $value = 0; foreach (array("cert", "ca") as $cert_type) { From bbca2fb11f719245cd7bd646d2ace4563f9eb011 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Wed, 16 Feb 2022 17:20:14 +0100 Subject: [PATCH 12/93] Simplify service values --- pfsense_zbx.php | 116 ++++++++++++++++++++++-------------------------- 1 file changed, 54 insertions(+), 62 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index b85994d..0a9e43c 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -142,6 +142,23 @@ define("IPSEC_PH1_VALUES", [ 'disabled' => fn() => "0", ]); +define("SERVICES_VALUES", [ + "status" => function ($service) { + $status = get_service_status($service); + + return ($status == "") ? 0 : $status; + }, + "name" => function ($service, $name) { + echo $name; + }, + "enabled" => function ($service, $name, $short_name) { + return pfz_bint(is_service_enabled($short_name)); + }, + "run_on_carp_slave" => function ($service, $name, $short_name, $carpcfr, $stopped_on_carp_slave) { + return pfz_bint(in_array($carpcfr, $stopped_on_carp_slave)); + } +]); + require_once('globals.inc'); require_once('functions.inc'); require_once('config.inc'); @@ -179,6 +196,10 @@ function pfz_cfg() { // Abstract global variable from code return $config; } +function pfz_bint(boolean $b) { + return (int)$b; +} + //Testing function, for template creating purpose function pfz_test(){ $line = "-------------------\n"; @@ -586,78 +607,49 @@ function pfz_services_discovery(){ // Get service value // 2020-03-27: Added space replace in service name for issue #12 // 2020-09-28: Corrected Space Replace -function pfz_service_value($name,$value){ - $services = get_services(); - $name = str_replace("__"," ",$name); - - //List of service which are stopped on CARP Slave. - //For now this is the best way i found for filtering out the triggers - //Waiting for a way in Zabbix to use Global Regexp in triggers with items discovery - $stopped_on_carp_slave = array("haproxy","radvd","openvpn.","openvpn","avahi"); - - foreach ($services as $service){ - $namecfr = $service["name"]; - $carpcfr = $service["name"]; +function pfz_service_value($name, $value) +{ + $services = get_services(); + $name = str_replace("__", " ", $name); - //OpenVPN - if (!empty($service['id'])) { - $namecfr = $service['name'] . "." . $service["id"]; - $carpcfr = $service['name'] . "."; - } + // List of service which are stopped on CARP Slave. + // For now this is the best way i found for filtering out the triggers + // Waiting for a way in Zabbix to use Global Regexp in triggers with items discovery + $stopped_on_carp_slave = array("haproxy", "radvd", "openvpn.", "openvpn", "avahi"); - //Captive Portal - if (!empty($service['zone'])) { - $namecfr = $service['name'] . "." . $service["zone"]; - $carpcfr = $service['name'] . "."; - } + $matching_services = array_filter($services, function ($server, $n) { + foreach (["id", "zone"] as $key) { + if (!empty($server[$key])) { + return printf("%s.%s", $server["name"], $server[$key]) == $n; + } + } - if ($namecfr == $name){ - switch ($value) { - - case "status": - $status = get_service_status($service); - if ($status=="") $status = 0; - echo $status; - break; + return false; + }); - case "name": - echo $namecfr; - break; + foreach ($matching_services as $service) { + $short_name = $service["name"]; + $carpcfr = $short_name . "."; - case "enabled": - if (is_service_enabled($service['name'])) - echo 1; - else - echo 0; - break; + $is_known_service_value = array_key_exists($value, SERVICES_VALUES); + if (!$is_known_service_value) { + echo $service[$value]; + continue; + } - case "run_on_carp_slave": - if (in_array($carpcfr,$stopped_on_carp_slave)) - echo 0; - else - echo 1; - break; - default: - echo $service[$value]; - break; - } - } + echo SERVICES_VALUES[$value]($service, $name, $short_name, $carpcfr, $stopped_on_carp_slave); } } - -//Gateway Discovery -function pfz_gw_rawstatus() { - // Return a Raw Gateway Status, useful for action Scripts (e.g. Update Cloudflare DNS config) - $gws = return_gateways_status(true); - $gw_string=""; - foreach ($gws as $gw){ - $gw_string .= ($gw['name'] . '.' . $gw['status'] .","); - } - echo rtrim($gw_string,","); +// Gateway Discovery +function pfz_gw_rawstatus() +{ + echo implode(",", + array_map( + fn($gw) => sprintf("%s.%s", $gw['name'], $gw['status']), + return_gateways_status(true))); } - function pfz_gw_discovery() { $gws = return_gateways_status(true); @@ -1209,7 +1201,7 @@ function pfz_get_system_value($section) $installed_version = $system_pkg_version["installed_version"]; if ($section === "new_version_available") { - echo pfz_bint($version, $installed_version); + echo pfz_bint($version != $installed_version); return; } From 37d4f1ac0a82c430ca2a794ca5931a928ab31d12 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Fri, 18 Feb 2022 09:44:15 +0100 Subject: [PATCH 13/93] Simplify Ipsec status --- pfsense_zbx.php | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 0a9e43c..003a9eb 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -797,7 +797,7 @@ function pfz_ipsec_ph2($uniqid, $valuekey){ echo $value; } -function pfz_ipsec_status($ikeid,$reqid=-1,$valuekey='state'){ +function pfz_ipsec_status($ike_id, $req_id=-1, $value_key='state'){ require_once("ipsec.inc"); $config = pfz_cfg(); @@ -831,7 +831,7 @@ function pfz_ipsec_status($ikeid,$reqid=-1,$valuekey='state'){ if (isset($ikesa['con-id'])) { $con_id = substr($ikesa['con-id'], 3); } else { - $con_id = filter_var($ikeid, FILTER_SANITIZE_NUMBER_INT); + $con_id = filter_var($ike_id, FILTER_SANITIZE_NUMBER_INT); } $con_name = "con" . $con_id; if ($ikesa['version'] == 1) { @@ -846,22 +846,22 @@ function pfz_ipsec_status($ikeid,$reqid=-1,$valuekey='state'){ $ipsecconnected[$con_id] = $ph1idx = $con_id; } } - if ($ph1idx == $ikeid){ - if ($reqid!=-1) { + if ($ph1idx == $ike_id){ + if ($req_id!=-1) { // Asking for Phase2 Status Value foreach ($ikesa['child-sas'] as $childsas) { - if ($childsas['reqid']==$reqid) { + if ($childsas['reqid']==$req_id) { if (strtolower($childsas['state']) == 'rekeyed') { //if state is rekeyed go on - $tmp_value = $childsas[$valuekey]; + $tmp_value = $childsas[$value_key]; } else { - $tmp_value = $childsas[$valuekey]; + $tmp_value = $childsas[$value_key]; break; } } } } else { - $tmp_value = $ikesa[$valuekey]; + $tmp_value = $ikesa[$value_key]; } break; @@ -869,17 +869,13 @@ function pfz_ipsec_status($ikeid,$reqid=-1,$valuekey='state'){ } } - switch($valuekey) { - case 'state': - $value = pfz_value_mapping('ipsec.state', strtolower($tmp_value)); - if ($carp_status!=0) $value = $value + (10 * ($carp_status-1)); - break; - default: - $value = $tmp_value; - break; - } - - return $value; + if ($value_key == "state") { + $v = pfz_value_mapping('ipsec.state', strtolower($tmp_value)); + + return ($carp_status!=0) ? $v + (10 * ($carp_status-1)) : $v; + } + + return $tmp_value; } // Temperature sensors Discovery From 17be728231509faa3a1f84fccd08e740e692f05c Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Fri, 18 Feb 2022 11:28:48 +0100 Subject: [PATCH 14/93] Introduce Command and Discovery classes --- pfsense_zbx.php | 2260 +++++++++++++++++++++++------------------------ 1 file changed, 1128 insertions(+), 1132 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 003a9eb..6db71cb 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1,63 +1,35 @@ + * This program is licensed under Apache 2.0 License + */ +require_once('globals.inc'); +require_once('functions.inc'); +require_once('config.inc'); +require_once('util.inc'); -Written by Riccardo Bicelli -This program is licensed under Apache 2.0 License -*/ +//For Interfaces Discovery +require_once('interfaces.inc'); -$exec_0 = fn(callable $f) => $f; -$exec_1 = fn(callable $f) => fn($parameters) => $f($parameters[0]); -$exec_2 = fn(callable $f) => fn($parameters) => $f($parameters[0], $parameters[1]); +//For OpenVPN Discovery +require_once('openvpn.inc'); + +//For Service Discovery +require_once("service-utils.inc"); + +//For System +require_once('pkg-utils.inc'); //Some Useful defines -define('SPEEDTEST_INTERVAL', 8); //Speedtest Interval (in hours) +define('SPEEDTEST_INTERVAL', 8); // Speedtest Interval (in hours) + +define("COMMAND_HANDLERS", build_method_lookup(PfzCommands::class)); // Argument parsers for Discovery -define('DISCOVERY_SECTION_HANDLERS', [ - "gw" => $exec_0(fn() => pfz_gw_discovery()), - "wan" => $exec_0(fn() => pfz_interface_discovery(true)), - "openvpn_server" => $exec_0(fn() => pfz_openvpn_serverdiscovery()), - "openvpn_server_user" => $exec_0(fn() => pfz_openvpn_server_userdiscovery()), - "openvpn_client" => $exec_0(fn() => pfz_openvpn_clientdiscovery()), - "services" => $exec_0(fn() => pfz_services_discovery()), - "interfaces" => $exec_0(fn() => pfz_interface_discovery()), - "ipsec_ph1" => $exec_0(fn() => pfz_ipsec_discovery_ph1()), - "ipsec_ph2" => $exec_0(fn() => pfz_ipsec_discovery_ph2()), - "dhcpfailover" => $exec_0(fn() => pfz_dhcpfailover_discovery()), - "temperature_sensors" => $exec_0(fn() => pfz_temperature_sensors_discovery()), -]); - -define('COMMAND_HANDLERS', [ - "carp_status" => $exec_0(fn() => pfz_carp_status()), - "cert_date" => $exec_1(fn($p0) => pfz_get_cert_date($p0)), - "cron_cleanup" => $exec_0(fn() => pfz_speedtest_cron_install(false)), - "dhcp" => $exec_2(fn($p0, $p1) => pfz_dhcp($p0, $p1)), - "discovery" => $exec_1(fn($p0) => pfz_discovery($p0)), - "file_exists" => $exec_1(fn($p0) => pfz_file_exists($p0)), - "gw_status" => $exec_0(fn() => pfz_gw_rawstatus()), - "gw_value" => $exec_2(fn($p0, $p1) => pfz_gw_value($p0, $p1)), - "if_name" => $exec_1(fn($p0) => pfz_get_if_name($p0)), - "if_speedtest_value" => $exec_2(function ($p0, $p1) { - pfz_speedtest_cron_install(); - pfz_interface_speedtest_value($p0, $p1); - }), - "ipsec_ph1" => $exec_2(fn($p0, $p1) => pfz_ipsec_ph1($p0, $p1)), - "ipsec_ph2" => $exec_2(fn($p0, $p1) => pfz_ipsec_ph2($p0, $p1)), - "openvpn_clientvalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_client_value($p0, $p1)), - "openvpn_server_uservalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_server_uservalue($p0, $p1)), - "openvpn_server_uservalue_numeric" => $exec_2(fn($p0, $p1) => pfz_openvpn_server_uservalue($p0, $p1, "0")), - "openvpn_servervalue" => $exec_2(fn($p0, $p1) => pfz_openvpn_server_value($p0, $p1)), - "service_value" => $exec_2(fn($p0, $p1) => pfz_service_value($p0, $p1)), - "speedtest_cron" => $exec_0(function () { - pfz_speedtest_cron_install(); - pfz_speedtest_cron(); - }), - "smart_status" => $exec_0(fn() => pfz_get_smart_status()), - "system" => $exec_1(fn($p0) => pfz_get_system_value($p0)), - "temperature" => $exec_1(fn($p0) => pfz_get_temperature($p0)), -]); +define('DISCOVERY_SECTION_HANDLERS', build_method_lookup(PfzDiscoveries::class)); define("VALUE_MAPPINGS", [ "openvpn.server.status" => [ @@ -126,19 +98,19 @@ define('SMART_DEV_STATUS', [ define("DHCP_SECTIONS", [ "failover" => function () { - echo pfz_dhcp_check_failover(); + echo self::pfz_dhcp_check_failover(); }, ]); define("OPENVPN_SERVER_VALUES", [ // Client Connections: is an array so it is sufficient to count elements "conns" => fn($server_value) => is_array($server_value) ? count($server_value) : 0, - "status" => fn($server_value) => pfz_value_mapping("openvpn.server.status", $server_value), - "mode" => fn($server_value) => pfz_value_mapping("openvpn.server.mode", $server_value) + "status" => fn($server_value) => self::pfz_value_mapping("openvpn.server.status", $server_value), + "mode" => fn($server_value) => self::pfz_value_mapping("openvpn.server.mode", $server_value) ]); define("IPSEC_PH1_VALUES", [ - 'status' => fn($ike_id) => pfz_ipsec_status($ike_id), + 'status' => fn($ike_id) => self::pfz_ipsec_status($ike_id), 'disabled' => fn() => "0", ]); @@ -152,59 +124,309 @@ define("SERVICES_VALUES", [ echo $name; }, "enabled" => function ($service, $name, $short_name) { - return pfz_bint(is_service_enabled($short_name)); + return Util::b2int(is_service_enabled($short_name)); }, "run_on_carp_slave" => function ($service, $name, $short_name, $carpcfr, $stopped_on_carp_slave) { - return pfz_bint(in_array($carpcfr, $stopped_on_carp_slave)); + return Util::b2int(in_array($carpcfr, $stopped_on_carp_slave)); } ]); -require_once('globals.inc'); -require_once('functions.inc'); -require_once('config.inc'); -require_once('util.inc'); - -//For Interfaces Discovery -require_once('interfaces.inc'); - -//For OpenVPN Discovery -require_once('openvpn.inc'); - -//For Service Discovery -require_once("service-utils.inc"); - -//For System -require_once('pkg-utils.inc'); - -//For DHCP - -// Utilities -function array_first(array $haystack, Callback $match) +class PfEnv { - foreach ($haystack as $needle) { - if ($match($needle)) { - return $needle; + public static function cfg() + { + // Abstract global variable from code + global $config; + + return $config; + } +} + +class Util +{ + public static function array_first(array $haystack, Callback $match) + { + foreach ($haystack as $needle) { + if ($match($needle)) { + return $needle; + } } + + return null; } - return null; + public static function b2int(bool $b): int + { + return (int)$b; + } + + public static function replace_special_chars($inputstr, $reverse = false) + { + $specialchars = ",',\",`,*,?,[,],{,},~,$,!,&,;,(,),<,>,|,#,@,0x0a"; + $specialchars = explode(",", $specialchars); + $resultstr = $inputstr; + + for ($n = 0; $n < count($specialchars); $n++) { + if ($reverse == false) + $resultstr = str_replace($specialchars[$n], '%%' . $n . '%', $resultstr); + else + $resultstr = str_replace('%%' . $n . '%', $specialchars[$n], $resultstr); + } + + return $resultstr; + } } -function pfz_cfg() { // Abstract global variable from code - global $config; +class PfzDiscoveries +{ + // Interface Discovery + // Improved performance + public static function pfz_interface_discovery($is_wan = false, $is_cron = false) + { + $ifdescrs = get_configured_interface_with_descr(true); + $ifaces = get_interface_arr(); + $ifcs = array(); + $if_ret = array(); - return $config; + $json_string = '{"data":['; + + foreach ($ifdescrs as $ifname => $ifdescr) { + $ifinfo = get_interface_info($ifname); + $ifinfo["description"] = $ifdescr; + $ifcs[$ifname] = $ifinfo; + } + + foreach ($ifaces as $hwif) { + + $ifdescr = $hwif; + $has_gw = false; + $is_vpn = false; + $has_public_ip = false; + + foreach ($ifcs as $ifc => $ifinfo) { + if ($ifinfo["hwif"] == $hwif) { + $ifdescr = $ifinfo["description"]; + if (array_key_exists("gateway", $ifinfo)) $has_gw = true; + // Issue #81 - https://stackoverflow.com/a/13818647/15093007 + if (filter_var($ifinfo["ipaddr"], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) $has_public_ip = true; + if (strpos($ifinfo["if"], "ovpn") !== false) $is_vpn = true; + break; + } + } + + if (($is_wan == false) || (($is_wan == true) && (($has_gw == true) || ($has_public_ip == true)) && ($is_vpn == false))) { + $if_ret[] = $hwif; + $json_string .= '{"{#IFNAME}":"' . $hwif . '"'; + $json_string .= ',"{#IFDESCR}":"' . $ifdescr . '"'; + $json_string .= '},'; + } + + } + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + if ($is_cron) return $if_ret; + + echo $json_string; + } + + public static function pfz_openvpn_serverdiscovery() + { + $servers = self::pfz_openvpn_get_all_servers(); + + $json_string = '{"data":['; + + foreach ($servers as $server) { + $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $server['name'])); + $json_string .= '{"{#SERVER}":"' . $server['vpnid'] . '"'; + $json_string .= ',"{#NAME}":"' . $name . '"'; + $json_string .= '},'; + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + } + + // OpenVPN Server/User-Auth Discovery + public static function pfz_openvpn_server_userdiscovery() + { + $servers = self::pfz_openvpn_get_all_servers(); + + $json_string = '{"data":['; + + foreach ($servers as $server) { + if (($server['mode'] == 'server_user') || ($server['mode'] == 'server_tls_user') || ($server['mode'] == 'server_tls')) { + if (is_array($server['conns'])) { + $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $server['name'])); + + foreach ($server['conns'] as $conn) { + + $common_name = Util::replace_special_chars($conn['common_name']); + + $json_string .= '{"{#SERVERID}":"' . $server['vpnid'] . '"'; + $json_string .= ',"{#SERVERNAME}":"' . $name . '"'; + $json_string .= ',"{#UNIQUEID}":"' . $server['vpnid'] . '+' . $common_name . '"'; + $json_string .= ',"{#USERID}":"' . $conn['common_name'] . '"'; + $json_string .= '},'; + } + } + } + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + } + + public static function pfz_gw_discovery() + { + $gws = return_gateways_status(true); + + $json_string = '{"data":['; + foreach ($gws as $gw) { + $json_string .= '{"{#GATEWAY}":"' . $gw['name'] . '"'; + $json_string .= '},'; + } + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + } + + // IPSEC Discovery + public static function pfz_ipsec_discovery_ph1() + { + + require_once("ipsec.inc"); + $config = PfEnv::cfg(); + init_config_arr(array('ipsec', 'phase1')); + $a_phase1 = &$config['ipsec']['phase1']; + + $json_string = '{"data":['; + + foreach ($a_phase1 as $data) { + $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; + $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; + $json_string .= '},'; + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + + } + + public static function pfz_ipsec_discovery_ph2() + { + + require_once("ipsec.inc"); + + $config = PfEnv::cfg(); + init_config_arr(array('ipsec', 'phase2')); + $a_phase2 = &$config['ipsec']['phase2']; + + $json_string = '{"data":['; + + foreach ($a_phase2 as $data) { + $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; + $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; + $json_string .= ',"{#UNIQID}":"' . $data['uniqid'] . '"'; + $json_string .= ',"{#REQID}":"' . $data['reqid'] . '"'; + $json_string .= ',"{#EXTID}":"' . $data['ikeid'] . '.' . $data['reqid'] . '"'; + $json_string .= '},'; + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + + } + + public static function pfz_dhcpfailover_discovery() + { + //System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait + require_once("system.inc"); + $leases = system_get_dhcpleases(); + + $json_string = '{"data":['; + + if (count($leases['failover']) > 0) { + foreach ($leases['failover'] as $data) { + $json_string .= '{"{#FAILOVER_GROUP}":"' . str_replace(" ", "__", $data['name']) . '"'; + } + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + } + + // OpenVPN Client Discovery + public static function pfz_openvpn_clientdiscovery() + { + $clients = openvpn_get_active_clients(); + + $json_string = '{"data":['; + + foreach ($clients as $client) { + $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $client['name'])); + $json_string .= '{"{#CLIENT}":"' . $client['vpnid'] . '"'; + $json_string .= ',"{#NAME}":"' . $name . '"'; + $json_string .= '},'; + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + } + + // Services Discovery + // 2020-03-27: Added space replace with __ for issue #12 + public static function pfz_services_discovery() + { + $services = get_services(); + + $json_string = '{"data":['; + + foreach ($services as $service) { + if (!empty($service['name'])) { + + $status = get_service_status($service); + if ($status = "") $status = 0; + + $id = ""; + //id for OpenVPN + if (!empty($service['id'])) $id = "." . $service["id"]; + //zone for Captive Portal + if (!empty($service['zone'])) $id = "." . $service["zone"]; + + $json_string .= '{"{#SERVICE}":"' . str_replace(" ", "__", $service['name']) . $id . '"'; + $json_string .= ',"{#DESCRIPTION}":"' . $service['description'] . '"'; + $json_string .= '},'; + } + } + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + } } -function pfz_bint(boolean $b) { - return (int)$b; -} - -//Testing function, for template creating purpose -function pfz_test(){ +class PfzCommands +{ + // Testing function, for template creating purpose + public static function pfz_test() + { $line = "-------------------\n"; - - $ovpn_servers = pfz_openvpn_get_all_servers(); + + $ovpn_servers = self::pfz_openvpn_get_all_servers(); echo "OPENVPN Servers:\n"; print_r($ovpn_servers); echo $line; @@ -214,1076 +436,850 @@ function pfz_test(){ print_r($ovpn_clients); echo $line; - $ifdescrs = get_configured_interface_with_descr(true); - $ifaces=array(); - foreach ($ifdescrs as $ifdescr => $ifname){ - $ifinfo = get_interface_info($ifdescr); - $ifaces[$ifname] = $ifinfo; + $ifdescrs = self::get_configured_interface_with_descr(true); + $ifaces = array(); + foreach ($ifdescrs as $ifdescr => $ifname) { + $ifinfo = self::get_interface_info($ifdescr); + $ifaces[$ifname] = $ifinfo; } - echo "Network Interfaces:\n"; + echo "Network Interfaces:\n"; print_r($ifaces); - print_r(get_interface_arr()); - print_r(get_configured_interface_list()); + print_r(self::get_interface_arr()); + print_r(self::get_configured_interface_list()); echo $line; - $services = get_services(); + $services = self::get_services(); echo "Services: \n"; print_r($services); echo $line; - + echo "IPsec: \n"; - - require_once("ipsec.inc"); - $config = pfz_cfg(); - init_config_arr(array('ipsec', 'phase1')); - init_config_arr(array('ipsec', 'phase2')); - $a_phase2 = &$config['ipsec']['phase2']; + + require_once("ipsec.inc"); + $config = PfEnv::cfg(); + self::init_config_arr(array('ipsec', 'phase1')); + self::init_config_arr(array('ipsec', 'phase2')); + $a_phase2 = &$config['ipsec']['phase2']; + $status = self::ipsec_list_sa(); + echo "IPsec Status: \n"; + print_r($status); + + $a_phase1 = &$config['ipsec']['phase1']; + $a_phase2 = &$config['ipsec']['phase2']; + + echo "IPsec Config Phase 1: \n"; + print_r($a_phase1); + + echo "IPsec Config Phase 2: \n"; + print_r($a_phase2); + + echo $line; + + //Packages + echo "Packages: \n"; + require_once("pkg-utils.inc"); + $installed_packages = self::get_pkg_info('all', false, true); + print_r($installed_packages); + } + + // Interface Speedtest + public static function pfz_interface_speedtest_value($ifname, $value) + { + $tvalue = explode(".", $value); + + if (count($tvalue) > 1) { + $value = $tvalue[0]; + $subvalue = $tvalue[1]; + } + + //If the interface has a gateway is considered WAN, so let's do the speedtest + $filename = "/tmp/speedtest-$ifname"; + + if (file_exists($filename)) { + $speedtest_data = json_decode(file_get_contents($filename), true); + + if (array_key_exists($value, $speedtest_data)) { + if ($subvalue == false) + echo $speedtest_data[$value]; + else + echo $speedtest_data[$value][$subvalue]; + } + } + + } + + // This is supposed to run via cron job + public static function pfz_speedtest_cron() + { + require_once("services.inc"); + $ifdescrs = get_configured_interface_with_descr(true); + $ifaces = get_interface_arr(); + $pf_interface_name = ''; + $subvalue = false; + + $ifcs = PfzDiscoveries::pfz_interface_discovery(true, true); + + foreach ($ifcs as $ifname) { + + foreach ($ifdescrs as $ifn => $ifd) { + $ifinfo = get_interface_info($ifn); + if ($ifinfo['hwif'] == $ifname) { + $pf_interface_name = $ifn; + break; + } + } + + self::pfz_speedtest_exec($ifname, $ifinfo['ipaddr']); + + } + } + + // Installs a cron job for speedtests + public static function pfz_speedtest_cron_install($enable = true) + { + //Install Cron Job + $command = "/usr/local/bin/php " . __FILE__ . " speedtest_cron"; + install_cron_job($command, $enable, $minute = "*/15", "*", "*", "*", "*", "root", true); + } + + public static function pfz_speedtest_exec($ifname, $ipaddr): bool + { + + $filename = "/tmp/speedtest-$ifname"; + $filetemp = "$filename.tmp"; + $filerun = "/tmp/speedtest-run"; + + // Issue #82 + // Sleep random delay in order to avoid problem when 2 pfSense on the same Internet line + sleep(rand(1, 90)); + + if ((time() - filemtime($filename) > SPEEDTEST_INTERVAL * 3600) || (file_exists($filename) == false)) { + // file is older than SPEEDTEST_INTERVAL + if ((time() - filemtime($filerun) > 180)) @unlink($filerun); + + if (file_exists($filerun) == false) { + touch($filerun); + $st_command = "/usr/local/bin/speedtest --source $ipaddr --json > $filetemp"; + exec($st_command); + rename($filetemp, $filename); + @unlink($filerun); + } + } + + return true; + } + + + // OpenVPN Server Discovery + public static function pfz_openvpn_get_all_servers() + { + $servers = openvpn_get_active_servers(); + $sk_servers = openvpn_get_active_servers("p2p"); + $servers = array_merge($servers, $sk_servers); + return ($servers); + } + + public static function pfz_retrieve_server_value($maybe_server, $value_key) + { + if (empty($maybe_server)) { + return null; + } + + $raw_value = $maybe_server[$value_key]; + + if (in_array($maybe_server["mode"], ["server_user", "server_tls_user", "server_tls"])) { + return $raw_value == "" ? "server_user_listening" : $raw_value; + } + + if ($maybe_server["mode"] == "p2p_tls") { + // For p2p_tls, ensure we have one client, and return up if it's the case + if ($raw_value == "") { + $has_at_least_one_connection = + is_array($maybe_server["conns"]) && count($maybe_server["conns"]) > 0; + + return $has_at_least_one_connection ? "up" : "down"; + } + } + + return $raw_value; + } + + // Get OpenVPN Server Value + public static function pfz_openvpn_server_value($server_id, $value_key) + { + $servers = self::pfz_openvpn_get_all_servers(); + + $maybe_server = Util::array_first($servers, fn($s) => $s['vpnid'] == $server_id); + + $server_value = self::pfz_retrieve_server_value($maybe_server, $value_key); + + $is_known_value_key = array_key_exists($value_key, OPENVPN_SERVER_VALUES); + if ($is_known_value_key) { + echo OPENVPN_SERVER_VALUES[$value_key]($server_value); + return; + } + + echo $server_value; + } + + // Get OpenVPN User Connected Value + public static function pfz_openvpn_server_uservalue($unique_id, $valuekey, $default = "") + { + $unique_id = Util::replace_special_chars($unique_id, true); + $atpos = strpos($unique_id, '+'); + $server_id = substr($unique_id, 0, $atpos); + $user_id = substr($unique_id, $atpos + 1); + + $servers = self::pfz_openvpn_get_all_servers(); + foreach ($servers as $server) { + if ($server['vpnid'] == $server_id) { + foreach ($server['conns'] as $conn) { + if ($conn['common_name'] == $user_id) { + $value = $conn[$valuekey]; + } + } + } + } + if ($value == "") $value = $default; + echo $value; + } + + public static function pfz_openvpn_client_value($client_id, $value_key, $fallback_value = "none") + { + $clients = openvpn_get_active_clients(); + + $client = Util::array_first($clients, fn($client) => $client['vpnid'] == $client_id); + + if (empty($client)) { + return $fallback_value; + } + + $maybe_value = $client[$value_key]; + + $is_known_value_key = array_key_exists($value_key, OPENVPN_CLIENT_VALUE); + if ($is_known_value_key) { + return OPENVPN_CLIENT_VALUE[$value_key]($maybe_value); + } + + return ($maybe_value == "") ? $fallback_value : $maybe_value; + } + + // Get service value + // 2020-03-27: Added space replace in service name for issue #12 + // 2020-09-28: Corrected Space Replace + public static function pfz_service_value($name, $value) + { + $services = get_services(); + $name = str_replace("__", " ", $name); + + // List of service which are stopped on CARP Slave. + // For now this is the best way i found for filtering out the triggers + // Waiting for a way in Zabbix to use Global Regexp in triggers with items discovery + $stopped_on_carp_slave = array("haproxy", "radvd", "openvpn.", "openvpn", "avahi"); + + $matching_services = array_filter($services, function ($server, $n) { + foreach (["id", "zone"] as $key) { + if (!empty($server[$key])) { + return printf("%s.%s", $server["name"], $server[$key]) == $n; + } + } + + return false; + }); + + foreach ($matching_services as $service) { + $short_name = $service["name"]; + $carpcfr = $short_name . "."; + + $is_known_service_value = array_key_exists($value, SERVICES_VALUES); + if (!$is_known_service_value) { + echo $service[$value]; + continue; + } + + echo SERVICES_VALUES[$value]($service, $name, $short_name, $carpcfr, $stopped_on_carp_slave); + } + } + + public static function pfz_gw_value($gw, $valuekey) + { + $gws = return_gateways_status(true); + if (array_key_exists($gw, $gws)) { + $value = $gws[$gw][$valuekey]; + if ($valuekey == "status") { + //Issue #70: Gateway Forced Down + if ($gws[$gw]["substatus"] <> "none") + $value = $gws[$gw]["substatus"]; + + $value = self::pfz_value_mapping("gateway.status", $value); + } + echo $value; + } + } + + public static function pfz_ipsec_ph1($ike_id, $value_key) + { + // Get Value from IPsec Phase 1 Configuration + // If Getting "disabled" value only check item presence in config array + require_once("ipsec.inc"); + $config = PfEnv::cfg(); + init_config_arr(array('ipsec', 'phase1')); + $a_phase1 = &$config['ipsec']['phase1']; + + $is_known_ipsec_key = array_key_exists($value_key, IPSEC_PH1_VALUES); + if ($is_known_ipsec_key) { + echo IPSEC_PH1_VALUES[$value_key]($ike_id); + return; + } + + $maybe_ike_match = Util::array_first($a_phase1, fn($d) => $d["ikeid"] == $ike_id); + if (empty($maybe_ike_match)) { + echo ""; + return; + } + + if (!array_key_exists($value_key, $maybe_ike_match)) { + echo ""; + return; + } + + if ($value_key == 'disabled') { + echo "1"; + return; + } + + echo self::pfz_value_mapping("ipsec." . $value_key, $maybe_ike_match[$value_key]); + } + + public static function pfz_ipsec_ph2($uniqid, $valuekey) + { + require_once("ipsec.inc"); + $config = PfEnv::cfg(); + init_config_arr(array('ipsec', 'phase2')); + $a_phase2 = &$config['ipsec']['phase2']; + + $valuecfr = explode(".", $valuekey); + + switch ($valuecfr[0]) { + case 'status': + $idarr = explode(".", $uniqid); + $statuskey = "state"; + if (isset($valuecfr[1])) $statuskey = $valuecfr[1]; + $value = self::pfz_ipsec_status($idarr[0], $idarr[1], $statuskey); + break; + case 'disabled': + $value = "0"; + } + + foreach ($a_phase2 as $data) { + if ($data['uniqid'] == $uniqid) { + if (array_key_exists($valuekey, $data)) { + if ($valuekey == 'disabled') + $value = "1"; + else + $value = self::pfz_value_mapping("ipsec_ph2." . $valuekey, $data[$valuekey], $data[$valuekey]); + break; + } + } + } + echo $value; + } + + public static function pfz_ipsec_status($ike_id, $req_id = -1, $value_key = 'state') + { + + require_once("ipsec.inc"); + $config = PfEnv::cfg(); + init_config_arr(array('ipsec', 'phase1')); + + $a_phase1 = &$config['ipsec']['phase1']; + $conmap = array(); + foreach ($a_phase1 as $ph1ent) { + if (function_exists('get_ipsecifnum')) { + if (get_ipsecifnum($ph1ent['ikeid'], 0)) { + $cname = "con" . get_ipsecifnum($ph1ent['ikeid'], 0); + } else { + $cname = "con{$ph1ent['ikeid']}00000"; + } + } else { + $cname = ipsec_conid($ph1ent); + } + + $conmap[$cname] = $ph1ent['ikeid']; + } + $status = ipsec_list_sa(); - echo "IPsec Status: \n"; - print_r($status); - - $a_phase1 = &$config['ipsec']['phase1']; - $a_phase2 = &$config['ipsec']['phase2']; - - echo "IPsec Config Phase 1: \n"; - print_r($a_phase1); - - echo "IPsec Config Phase 2: \n"; - print_r($a_phase2); - - echo $line; - - //Packages - echo "Packages: \n"; - require_once("pkg-utils.inc"); - $installed_packages = get_pkg_info('all', false, true); - print_r($installed_packages); -} + $ipsecconnected = array(); + + $carp_status = self::pfz_carp_status(false); + + //Phase-Status match borrowed from status_ipsec.php + if (is_array($status)) { + foreach ($status as $l_ikeid => $ikesa) { + + if (isset($ikesa['con-id'])) { + $con_id = substr($ikesa['con-id'], 3); + } else { + $con_id = filter_var($ike_id, FILTER_SANITIZE_NUMBER_INT); + } + $con_name = "con" . $con_id; + if ($ikesa['version'] == 1) { + $ph1idx = $conmap[$con_name]; + $ipsecconnected[$ph1idx] = $ph1idx; + } else { + if (!ipsec_ikeid_used($con_id)) { + // probably a v2 with split connection then + $ph1idx = $conmap[$con_name]; + $ipsecconnected[$ph1idx] = $ph1idx; + } else { + $ipsecconnected[$con_id] = $ph1idx = $con_id; + } + } + if ($ph1idx == $ike_id) { + if ($req_id != -1) { + // Asking for Phase2 Status Value + foreach ($ikesa['child-sas'] as $childsas) { + if ($childsas['reqid'] == $req_id) { + if (strtolower($childsas['state']) == 'rekeyed') { + //if state is rekeyed go on + $tmp_value = $childsas[$value_key]; + } else { + $tmp_value = $childsas[$value_key]; + break; + } + } + } + } else { + $tmp_value = $ikesa[$value_key]; + } + + break; + } + } + } + + if ($value_key == "state") { + $v = self::pfz_value_mapping('ipsec.state', strtolower($tmp_value)); + + return ($carp_status != 0) ? $v + (10 * ($carp_status - 1)) : $v; + } + + return $tmp_value; + } + + function pfz_temperature_sensors_discovery() + { -// Interface Discovery -// Improved performance -function pfz_interface_discovery($is_wan=false,$is_cron=false) { - $ifdescrs = get_configured_interface_with_descr(true); - $ifaces = get_interface_arr(); - $ifcs=array(); - $if_ret=array(); - - $json_string = '{"data":['; - - foreach ($ifdescrs as $ifname => $ifdescr){ - $ifinfo = get_interface_info($ifname); - $ifinfo["description"] = $ifdescr; - $ifcs[$ifname] = $ifinfo; - } + $json_string = '{"data":['; + $sensors = []; + exec("sysctl -a | grep temperature | cut -d ':' -f 1", $sensors, $code); + if ($code != 0) { + echo ""; + return; + } else { + foreach ($sensors as $sensor) { + $json_string .= '{"{#SENSORID}":"' . $sensor . '"'; + $json_string .= '},'; + } + } - foreach ($ifaces as $hwif) { - - $ifdescr = $hwif; - $has_gw = false; - $is_vpn = false; - $has_public_ip = false; - - foreach($ifcs as $ifc=>$ifinfo){ - if ($ifinfo["hwif"] == $hwif){ - $ifdescr = $ifinfo["description"]; - if (array_key_exists("gateway",$ifinfo)) $has_gw=true; - // Issue #81 - https://stackoverflow.com/a/13818647/15093007 - if (filter_var($ifinfo["ipaddr"], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) $has_public_ip=true; - if (strpos($ifinfo["if"],"ovpn")!==false) $is_vpn=true; + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + + } + + function pfz_get_temperature($sensorid) + { + + exec("sysctl '$sensorid' | cut -d ':' -f 2", $value, $code); + if ($code != 0 or count($value) != 1) { + echo ""; + return; + } else { + echo trim($value[0]); + } + + } + + public static function pfz_carp_status($echo = true) + { + //Detect CARP Status + $config = PfEnv::cfg(); + $status_return = 0; + $status = get_carp_status(); + $carp_detected_problems = get_single_sysctl("net.inet.carp.demotion"); + + //CARP is disabled + $ret = 0; + + if ($status != 0) { //CARP is enabled + + if ($carp_detected_problems != 0) { + //There's some Major Problems with CARP + $ret = 4; + if ($echo == true) echo $ret; + return $ret; + } + + $status_changed = false; + $prev_status = ""; + foreach ($config['virtualip']['vip'] as $carp) { + if ($carp['mode'] != "carp") { + continue; + } + $if_status = get_carp_interface_status("_vip{$carp['uniqid']}"); + + if (($prev_status != $if_status) && (empty($if_status) == false)) { //Some glitches with GUI + if ($prev_status != "") $status_changed = true; + $prev_status = $if_status; + } + } + if ($status_changed) { + //CARP Status is inconsistent across interfaces + $ret = 3; + echo 3; + } else { + if ($prev_status == "MASTER") + $ret = 1; + else + $ret = 2; + } + } + + if ($echo == true) echo $ret; + return $ret; + + } + + // DHCP Checks (copy of status_dhcp_leases.php, waiting for pfsense 2.5) + public static function pfz_remove_duplicate($array, $field) + { + foreach ($array as $sub) { + $cmp[] = $sub[$field]; + } + $unique = array_unique(array_reverse($cmp, true)); + foreach ($unique as $k => $rien) { + $new[] = $array[$k]; + } + return $new; + } + + // Get DHCP Arrays (copied from status_dhcp_leases.php, waiting for pfsense 2.5, in order to use system_get_dhcpleases();) + public static function pfz_dhcp_get($valuekey) + { + + require_once("config.inc"); + + $leasesfile = "{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases"; + + $awk = "/usr/bin/awk"; + /* this pattern sticks comments into a single array item */ + $cleanpattern = "'{ gsub(\"#.*\", \"\");} { gsub(\";\", \"\"); print;}'"; + /* We then split the leases file by } */ + $splitpattern = "'BEGIN { RS=\"}\";} {for (i=1; i<=NF; i++) printf \"%s \", \$i; printf \"}\\n\";}'"; + + /* stuff the leases file in a proper format into a array by line */ + @exec("/bin/cat {$leasesfile} 2>/dev/null| {$awk} {$cleanpattern} | {$awk} {$splitpattern}", $leases_content); + $leases_count = count($leases_content); + @exec("/usr/sbin/arp -an", $rawdata); + + foreach ($leases_content as $lease) { + /* split the line by space */ + $data = explode(" ", $lease); + /* walk the fields */ + $f = 0; + $fcount = count($data); + /* with less than 20 fields there is nothing useful */ + if ($fcount < 20) { + $i++; + continue; + } + while ($f < $fcount) { + switch ($data[$f]) { + case "failover": + $pools[$p]['name'] = trim($data[$f + 2], '"'); + $pools[$p]['name'] = "{$pools[$p]['name']} (" . convert_friendly_interface_to_friendly_descr(substr($pools[$p]['name'], 5)) . ")"; + $pools[$p]['mystate'] = $data[$f + 7]; + $pools[$p]['peerstate'] = $data[$f + 14]; + $pools[$p]['mydate'] = $data[$f + 10]; + $pools[$p]['mydate'] .= " " . $data[$f + 11]; + $pools[$p]['peerdate'] = $data[$f + 17]; + $pools[$p]['peerdate'] .= " " . $data[$f + 18]; + $p++; + $i++; + continue 3; + case "lease": + $leases[$l]['ip'] = $data[$f + 1]; + $leases[$l]['type'] = $dynamic_string; + $f = $f + 2; + break; + case "starts": + $leases[$l]['start'] = $data[$f + 2]; + $leases[$l]['start'] .= " " . $data[$f + 3]; + $f = $f + 3; + break; + case "ends": + if ($data[$f + 1] == "never") { + // Quote from dhcpd.leases(5) man page: + // If a lease will never expire, date is never instead of an actual date. + $leases[$l]['end'] = gettext("Never"); + $f = $f + 1; + } else { + $leases[$l]['end'] = $data[$f + 2]; + $leases[$l]['end'] .= " " . $data[$f + 3]; + $f = $f + 3; + } + break; + case "tstp": + $f = $f + 3; + break; + case "tsfp": + $f = $f + 3; + break; + case "atsfp": + $f = $f + 3; + break; + case "cltt": + $f = $f + 3; + break; + case "binding": + switch ($data[$f + 2]) { + case "active": + $leases[$l]['act'] = $active_string; + break; + case "free": + $leases[$l]['act'] = $expired_string; + $leases[$l]['online'] = $offline_string; + break; + case "backup": + $leases[$l]['act'] = $reserved_string; + $leases[$l]['online'] = $offline_string; + break; + } + $f = $f + 1; + break; + case "next": + /* skip the next binding statement */ + $f = $f + 3; + break; + case "rewind": + /* skip the rewind binding statement */ + $f = $f + 3; + break; + case "hardware": + $leases[$l]['mac'] = $data[$f + 2]; + /* check if it's online and the lease is active */ + if (in_array($leases[$l]['ip'], $arpdata_ip)) { + $leases[$l]['online'] = $online_string; + } else { + $leases[$l]['online'] = $offline_string; + } + $f = $f + 2; + break; + case "client-hostname": + if ($data[$f + 1] <> "") { + $leases[$l]['hostname'] = preg_replace('/"/', '', $data[$f + 1]); + } else { + $hostname = gethostbyaddr($leases[$l]['ip']); + if ($hostname <> "") { + $leases[$l]['hostname'] = $hostname; + } + } + $f = $f + 1; + break; + case "uid": + $f = $f + 1; break; } - } - - if ( ($is_wan==false) || (($is_wan==true) && (($has_gw==true) || ($has_public_ip==true)) && ($is_vpn==false)) ) { - $if_ret[]=$hwif; - $json_string .= '{"{#IFNAME}":"' . $hwif . '"'; - $json_string .= ',"{#IFDESCR}":"' . $ifdescr . '"'; - $json_string .= '},'; - } - - } - $json_string = rtrim($json_string,","); - $json_string .= "]}"; - - if ($is_cron) return $if_ret; - - echo $json_string; -} - - -//Interface Speedtest -function pfz_interface_speedtest_value($ifname, $value){ - $tvalue = explode(".", $value); - - if (count($tvalue)>1) { - $value = $tvalue[0]; - $subvalue = $tvalue[1]; - } - - //If the interface has a gateway is considered WAN, so let's do the speedtest - $filename = "/tmp/speedtest-$ifname"; - - if (file_exists($filename)) { - $speedtest_data = json_decode(file_get_contents($filename), true); - - if (array_key_exists($value, $speedtest_data)) { - if ($subvalue == false) - echo $speedtest_data[$value]; - else - echo $speedtest_data[$value][$subvalue]; - } - } - -} - -// This is supposed to run via cron job -function pfz_speedtest_cron(){ - require_once("services.inc"); - $ifdescrs = get_configured_interface_with_descr(true); - $ifaces = get_interface_arr(); - $pf_interface_name=''; - $subvalue=false; - - $ifcs = pfz_interface_discovery(true, true); - - foreach ($ifcs as $ifname) { - - foreach ($ifdescrs as $ifn => $ifd){ - $ifinfo = get_interface_info($ifn); - if($ifinfo['hwif']==$ifname) { - $pf_interface_name = $ifn; - break; - } - } - - pfz_speedtest_exec($ifname, $ifinfo['ipaddr']); - - } -} - -//installs a cron job for speedtests -function pfz_speedtest_cron_install($enable=true){ - //Install Cron Job - $command = "/usr/local/bin/php " . __FILE__ . " speedtest_cron"; - install_cron_job($command, $enable, $minute = "*/15", "*", "*", "*", "*", "root", true); -} - - -function pfz_speedtest_exec ($ifname, $ipaddr){ - - $filename = "/tmp/speedtest-$ifname"; - $filetemp = "$filename.tmp"; - $filerun = "/tmp/speedtest-run"; - - // Issue #82 - // Sleep random delay in order to avoid problem when 2 pfSense on the same Internet line - sleep (rand ( 1, 90)); - - if ( (time()-filemtime($filename) > SPEEDTEST_INTERVAL * 3600) || (file_exists($filename)==false) ) { - // file is older than SPEEDTEST_INTERVAL - if ( (time()-filemtime($filerun) > 180 ) ) @unlink($filerun); - - if (file_exists($filerun)==false) { - touch($filerun); - $st_command = "/usr/local/bin/speedtest --source $ipaddr --json > $filetemp"; - exec ($st_command); - rename($filetemp,$filename); - @unlink($filerun); - } - } - - return true; -} - - -// OpenVPN Server Discovery -function pfz_openvpn_get_all_servers(){ - $servers = openvpn_get_active_servers(); - $sk_servers = openvpn_get_active_servers("p2p"); - $servers = array_merge($servers,$sk_servers); - return ($servers); -} - - -function pfz_openvpn_serverdiscovery() { - $servers = pfz_openvpn_get_all_servers(); - - $json_string = '{"data":['; - - foreach ($servers as $server){ - $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $server['name'])); - $json_string .= '{"{#SERVER}":"' . $server['vpnid'] . '"'; - $json_string .= ',"{#NAME}":"' . $name . '"'; - $json_string .= '},'; - } - - $json_string = rtrim($json_string,","); - $json_string .= "]}"; - - echo $json_string; -} - -function pfz_retrieve_server_value($maybe_server, $value_key) -{ - if (empty($maybe_server)) { - return null; - } - - $raw_value = $maybe_server[$value_key]; - - if (in_array($maybe_server["mode"], ["server_user", "server_tls_user", "server_tls"])) { - return $raw_value == "" ? "server_user_listening" : $raw_value; - } - - if ($maybe_server["mode"] == "p2p_tls") { - // For p2p_tls, ensure we have one client, and return up if it's the case - if ($raw_value == "") { - $has_at_least_one_connection = - is_array($maybe_server["conns"]) && count($maybe_server["conns"]) > 0; - - return $has_at_least_one_connection ? "up" : "down"; - } - } - - return $raw_value; -} - -// Get OpenVPN Server Value -function pfz_openvpn_server_value($server_id, $value_key) -{ - $servers = pfz_openvpn_get_all_servers(); - - $maybe_server = array_first($servers, fn($s) => $s['vpnid'] == $server_id); - - $server_value = pfz_retrieve_server_value($maybe_server, $value_key); - - $is_known_value_key = array_key_exists($value_key, OPENVPN_SERVER_VALUES); - if ($is_known_value_key) { - echo OPENVPN_SERVER_VALUES[$value_key]($server_value); - return; - } - - echo $server_value; -} - -//OpenVPN Server/User-Auth Discovery -function pfz_openvpn_server_userdiscovery(){ - $servers = pfz_openvpn_get_all_servers(); - - $json_string = '{"data":['; - - foreach ($servers as $server){ - if ( ($server['mode']=='server_user') || ($server['mode']=='server_tls_user') || ($server['mode']=='server_tls') ) { - if (is_array($server['conns'])) { - $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $server['name'])); - - foreach($server['conns'] as $conn) { - - $common_name = pfz_replacespecialchars($conn['common_name']); - - $json_string .= '{"{#SERVERID}":"' . $server['vpnid'] . '"'; - $json_string .= ',"{#SERVERNAME}":"' . $name . '"'; - $json_string .= ',"{#UNIQUEID}":"' . $server['vpnid'] . '+' . $common_name . '"'; - $json_string .= ',"{#USERID}":"' . $conn['common_name'] . '"'; - $json_string .= '},'; - } - } - } - } - - $json_string = rtrim($json_string,","); - $json_string .= "]}"; - - echo $json_string; -} - -// Get OpenVPN User Connected Value -function pfz_openvpn_server_uservalue($unique_id, $valuekey, $default=""){ - - $unique_id = pfz_replacespecialchars($unique_id,true); - $atpos=strpos($unique_id,'+'); - $server_id = substr($unique_id,0,$atpos); - $user_id = substr($unique_id,$atpos+1); - - $servers = pfz_openvpn_get_all_servers(); - foreach($servers as $server) { - if($server['vpnid']==$server_id) { - foreach($server['conns'] as $conn) { - if ($conn['common_name']==$user_id){ - $value = $conn[$valuekey]; - } - } - } - } - if ($value=="") $value = $default; - echo $value; -} -// OpenVPN Client Discovery -function pfz_openvpn_clientdiscovery() { - $clients = openvpn_get_active_clients(); - - $json_string = '{"data":['; - - foreach ($clients as $client){ - $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $client['name'])); - $json_string .= '{"{#CLIENT}":"' . $client['vpnid'] . '"'; - $json_string .= ',"{#NAME}":"' . $name . '"'; - $json_string .= '},'; - } - - $json_string = rtrim($json_string,","); - $json_string .= "]}"; - - echo $json_string; -} - -function pfz_replacespecialchars($inputstr,$reverse=false){ - $specialchars = ",',\",`,*,?,[,],{,},~,$,!,&,;,(,),<,>,|,#,@,0x0a"; - $specialchars = explode(",",$specialchars); - $resultstr = $inputstr; - - for ($n=0;$n $client['vpnid'] == $client_id); - - if (empty($client)) { - return $fallback_value; - } - - $maybe_value = $client[$value_key]; - - $is_known_value_key = array_key_exists($value_key, OPENVPN_CLIENT_VALUE); - if ($is_known_value_key) { - return OPENVPN_CLIENT_VALUE[$value_key]($maybe_value); - } - - return ($maybe_value == "") ? $fallback_value : $maybe_value; -} - -// Services Discovery -// 2020-03-27: Added space replace with __ for issue #12 -function pfz_services_discovery(){ - $services = get_services(); - - $json_string = '{"data":['; - - foreach ($services as $service){ - if (!empty($service['name'])) { - - $status = get_service_status($service); - if ($status="") $status = 0; - - $id=""; - //id for OpenVPN - if (!empty($service['id'])) $id = "." . $service["id"]; - //zone for Captive Portal - if (!empty($service['zone'])) $id = "." . $service["zone"]; - - $json_string .= '{"{#SERVICE}":"' . str_replace(" ", "__", $service['name']) . $id . '"'; - $json_string .= ',"{#DESCRIPTION}":"' . $service['description'] . '"'; - $json_string .= '},'; - } - } - $json_string = rtrim($json_string,","); - $json_string .= "]}"; - - echo $json_string; - -} - -// Get service value -// 2020-03-27: Added space replace in service name for issue #12 -// 2020-09-28: Corrected Space Replace -function pfz_service_value($name, $value) -{ - $services = get_services(); - $name = str_replace("__", " ", $name); - - // List of service which are stopped on CARP Slave. - // For now this is the best way i found for filtering out the triggers - // Waiting for a way in Zabbix to use Global Regexp in triggers with items discovery - $stopped_on_carp_slave = array("haproxy", "radvd", "openvpn.", "openvpn", "avahi"); - - $matching_services = array_filter($services, function ($server, $n) { - foreach (["id", "zone"] as $key) { - if (!empty($server[$key])) { - return printf("%s.%s", $server["name"], $server[$key]) == $n; + $f++; } + $l++; + $i++; + /* slowly chisel away at the source array */ + array_shift($leases_content); + } + /* remove duplicate items by mac address */ + if (count($leases) > 0) { + $leases = self::pfz_remove_duplicate($leases, "ip"); } - return false; - }); - - foreach ($matching_services as $service) { - $short_name = $service["name"]; - $carpcfr = $short_name . "."; - - $is_known_service_value = array_key_exists($value, SERVICES_VALUES); - if (!$is_known_service_value) { - echo $service[$value]; - continue; + if (count($pools) > 0) { + $pools = self::pfz_remove_duplicate($pools, "name"); + asort($pools); } - echo SERVICES_VALUES[$value]($service, $name, $short_name, $carpcfr, $stopped_on_carp_slave); - } -} - -// Gateway Discovery -function pfz_gw_rawstatus() -{ - echo implode(",", - array_map( - fn($gw) => sprintf("%s.%s", $gw['name'], $gw['status']), - return_gateways_status(true))); -} - -function pfz_gw_discovery() { - $gws = return_gateways_status(true); - - $json_string = '{"data":['; - foreach ($gws as $gw){ - $json_string .= '{"{#GATEWAY}":"' . $gw['name'] . '"'; - $json_string .= '},'; - } - $json_string = rtrim($json_string,","); - $json_string .= "]}"; - - echo $json_string; -} - - -function pfz_gw_value($gw, $valuekey) { - $gws = return_gateways_status(true); - if(array_key_exists($gw,$gws)) { - $value = $gws[$gw][$valuekey]; - if ($valuekey=="status") { - //Issue #70: Gateway Forced Down - if ($gws[$gw]["substatus"]<>"none") - $value = $gws[$gw]["substatus"]; - - $value = pfz_value_mapping("gateway.status", $value); - } - echo $value; - } -} - - -// IPSEC Discovery -function pfz_ipsec_discovery_ph1(){ - - require_once("ipsec.inc"); - $config = pfz_cfg(); - init_config_arr(array('ipsec', 'phase1')); - $a_phase1 = &$config['ipsec']['phase1']; - - $json_string = '{"data":['; - - foreach ($a_phase1 as $data) { - $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; - $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; - $json_string .= '},'; - } - - $json_string = rtrim($json_string,","); - $json_string .= "]}"; - - echo $json_string; - -} - -function pfz_ipsec_ph1($ike_id, $value_key) -{ - // Get Value from IPsec Phase 1 Configuration - // If Getting "disabled" value only check item presence in config array - require_once("ipsec.inc"); - $config = pfz_cfg(); - init_config_arr(array('ipsec', 'phase1')); - $a_phase1 = &$config['ipsec']['phase1']; - - $is_known_ipsec_key = array_key_exists($value_key, IPSEC_PH1_VALUES); - if ($is_known_ipsec_key) { - echo IPSEC_PH1_VALUES[$value_key]($ike_id); - return; - } - - $maybe_ike_match = array_first($a_phase1, fn($d) => $d["ikeid"] == $ike_id); - if (empty($maybe_ike_match)) { - echo ""; - return; - } - - if (!array_key_exists($value_key, $maybe_ike_match)) { - echo ""; - return; - } - - if ($value_key == 'disabled') { - echo "1"; - return; - } - - echo pfz_value_mapping("ipsec." . $value_key, $maybe_ike_match[$value_key]); -} - -function pfz_ipsec_discovery_ph2(){ - - require_once("ipsec.inc"); - - $config = pfz_cfg(); - init_config_arr(array('ipsec', 'phase2')); - $a_phase2 = &$config['ipsec']['phase2']; - - $json_string = '{"data":['; - - foreach ($a_phase2 as $data) { - $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; - $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; - $json_string .= ',"{#UNIQID}":"' . $data['uniqid'] . '"'; - $json_string .= ',"{#REQID}":"' . $data['reqid'] . '"'; - $json_string .= ',"{#EXTID}":"' . $data['ikeid'] . '.' . $data['reqid'] . '"'; - $json_string .= '},'; - } - - $json_string = rtrim($json_string,","); - $json_string .= "]}"; - - echo $json_string; - -} - -function pfz_ipsec_ph2($uniqid, $valuekey){ - require_once("ipsec.inc"); - $config = pfz_cfg(); - init_config_arr(array('ipsec', 'phase2')); - $a_phase2 = &$config['ipsec']['phase2']; - - $valuecfr = explode(".",$valuekey); - - switch ($valuecfr[0]) { - case 'status': - $idarr = explode(".", $uniqid); - $statuskey = "state"; - if (isset($valuecfr[1])) $statuskey = $valuecfr[1]; - $value = pfz_ipsec_status($idarr[0],$idarr[1],$statuskey); - break; - case 'disabled': - $value = "0"; - } - - foreach ($a_phase2 as $data) { - if ($data['uniqid'] == $uniqid) { - if(array_key_exists($valuekey,$data)) { - if ($valuekey=='disabled') - $value = "1"; - else - $value = pfz_value_mapping("ipsec_ph2." . $valuekey, $data[$valuekey], $data[$valuekey]); - break; - } - } - } - echo $value; -} - -function pfz_ipsec_status($ike_id, $req_id=-1, $value_key='state'){ - - require_once("ipsec.inc"); - $config = pfz_cfg(); - init_config_arr(array('ipsec', 'phase1')); - - $a_phase1 = &$config['ipsec']['phase1']; - $conmap = array(); - foreach ($a_phase1 as $ph1ent) { - if (function_exists('get_ipsecifnum')) { - if (get_ipsecifnum($ph1ent['ikeid'], 0)) { - $cname = "con" . get_ipsecifnum($ph1ent['ikeid'], 0); - } else { - $cname = "con{$ph1ent['ikeid']}00000"; - } - } else{ - $cname = ipsec_conid($ph1ent); + switch ($valuekey) { + case "pools": + return $pools; + break; + case "failover": + return $failover; + break; + case "leases": + default: + return $leases; } - - $conmap[$cname] = $ph1ent['ikeid']; + } - $status = ipsec_list_sa(); - $ipsecconnected = array(); - - $carp_status = pfz_carp_status(false); - - //Phase-Status match borrowed from status_ipsec.php - if (is_array($status)) { - foreach ($status as $l_ikeid=>$ikesa) { - - if (isset($ikesa['con-id'])) { - $con_id = substr($ikesa['con-id'], 3); - } else { - $con_id = filter_var($ike_id, FILTER_SANITIZE_NUMBER_INT); - } - $con_name = "con" . $con_id; - if ($ikesa['version'] == 1) { - $ph1idx = $conmap[$con_name]; - $ipsecconnected[$ph1idx] = $ph1idx; - } else { - if (!ipsec_ikeid_used($con_id)) { - // probably a v2 with split connection then - $ph1idx = $conmap[$con_name]; - $ipsecconnected[$ph1idx] = $ph1idx; - } else { - $ipsecconnected[$con_id] = $ph1idx = $con_id; - } - } - if ($ph1idx == $ike_id){ - if ($req_id!=-1) { - // Asking for Phase2 Status Value - foreach ($ikesa['child-sas'] as $childsas) { - if ($childsas['reqid']==$req_id) { - if (strtolower($childsas['state']) == 'rekeyed') { - //if state is rekeyed go on - $tmp_value = $childsas[$value_key]; - } else { - $tmp_value = $childsas[$value_key]; - break; - } - } - } - } else { - $tmp_value = $ikesa[$value_key]; - } - - break; - } - } - } - - if ($value_key == "state") { - $v = pfz_value_mapping('ipsec.state', strtolower($tmp_value)); - - return ($carp_status!=0) ? $v + (10 * ($carp_status-1)) : $v; + // Gateway Discovery + public static function pfz_gw_rawstatus() + { + echo implode(",", + array_map( + fn($gw) => sprintf("%s.%s", $gw['name'], $gw['status']), + self::return_gateways_status(true))); } - - return $tmp_value; -} -// Temperature sensors Discovery -function pfz_temperature_sensors_discovery(){ + public static function pfz_dhcp_check_failover() + { + // Check DHCP Failover Status + // Returns number of failover pools which state is not normal or + // different than peer state + $failover = self::pfz_dhcp_get("failover"); + + return count(array_filter($failover, fn($f) => ($f["mystate"] != "normal") || ($f["mystate"] != $f["peerstate"]))); + } + + public static function pfz_dhcp($section, $valuekey = "") + { + $is_known_section = array_key_exists($section, DHCP_SECTIONS); + if (!$is_known_section) { + return; + } + + DHCP_SECTIONS[$section](); + } + + // Packages + public static function pfz_packages_uptodate() + { + require_once("pkg-utils.inc"); + $installed_packages = self::get_pkg_info("all", false, true); - $json_string = '{"data":['; - $sensors = []; - exec("sysctl -a | grep temperature | cut -d ':' -f 1", $sensors, $code); - if ($code != 0) { - echo ""; - return; - } else { - foreach ($sensors as $sensor) { - $json_string .= '{"{#SENSORID}":"' . $sensor . '"'; - $json_string .= '},'; + return count(array_filter( + $installed_packages, + fn($p) => $p["version"] != $p["installed_version"])); + } + + // System Information + public static function pfz_get_system_value($section) + { + if ($section === "packages_update") { + echo self::pfz_packages_uptodate(); + return; + } + + $system_pkg_version = self::get_system_pkg_version(); + $version = $system_pkg_version["version"]; + $installed_version = $system_pkg_version["installed_version"]; + + if ($section === "new_version_available") { + echo Util::b2int($version != $installed_version); + return; + } + + if (array_key_exists($section, $system_pkg_version)) { + echo $system_pkg_version[$section]; } } - $json_string = rtrim($json_string,","); - $json_string .= "]}"; - - echo $json_string; - -} - -// Temperature sensor get value -function pfz_get_temperature($sensorid){ - - exec("sysctl '$sensorid' | cut -d ':' -f 2", $value, $code); - if ($code != 0 or count($value)!=1) { - echo ""; - return; - } else { - echo trim($value[0]); - } - -} - - -function pfz_carp_status($echo = true){ - //Detect CARP Status - $config = pfz_cfg(); - $status_return = 0; - $status = get_carp_status(); - $carp_detected_problems = get_single_sysctl("net.inet.carp.demotion"); - - //CARP is disabled - $ret = 0; - - if ($status != 0) { //CARP is enabled - - if ($carp_detected_problems != 0) { - //There's some Major Problems with CARP - $ret = 4; - if ($echo == true) echo $ret; - return $ret; - } - - $status_changed = false; - $prev_status = ""; - foreach ($config['virtualip']['vip'] as $carp) { - if ($carp['mode'] != "carp") { - continue; - } - $if_status = get_carp_interface_status("_vip{$carp['uniqid']}"); - - if ( ($prev_status != $if_status) && (empty($if_status)==false) ) { //Some glitches with GUI - if ($prev_status!="") $status_changed = true; - $prev_status = $if_status; - } - } - if ($status_changed) { - //CARP Status is inconsistent across interfaces - $ret=3; - echo 3; - } else { - if ($prev_status=="MASTER") - $ret = 1; - else - $ret = 2; - } - } - - if ($echo == true) echo $ret; - return $ret; - -} - -// DHCP Checks (copy of status_dhcp_leases.php, waiting for pfsense 2.5) -function pfz_remove_duplicate($array, $field) { - foreach ($array as $sub) { - $cmp[] = $sub[$field]; - } - $unique = array_unique(array_reverse($cmp, true)); - foreach ($unique as $k => $rien) { - $new[] = $array[$k]; - } - return $new; -} - -// Get DHCP Arrays (copied from status_dhcp_leases.php, waiting for pfsense 2.5, in order to use system_get_dhcpleases();) -function pfz_dhcp_get($valuekey) { - - require_once("config.inc"); - - $leasesfile = "{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases"; - - $awk = "/usr/bin/awk"; - /* this pattern sticks comments into a single array item */ - $cleanpattern = "'{ gsub(\"#.*\", \"\");} { gsub(\";\", \"\"); print;}'"; - /* We then split the leases file by } */ - $splitpattern = "'BEGIN { RS=\"}\";} {for (i=1; i<=NF; i++) printf \"%s \", \$i; printf \"}\\n\";}'"; - - /* stuff the leases file in a proper format into a array by line */ - @exec("/bin/cat {$leasesfile} 2>/dev/null| {$awk} {$cleanpattern} | {$awk} {$splitpattern}", $leases_content); - $leases_count = count($leases_content); - @exec("/usr/sbin/arp -an", $rawdata); - - foreach ($leases_content as $lease) { - /* split the line by space */ - $data = explode(" ", $lease); - /* walk the fields */ - $f = 0; - $fcount = count($data); - /* with less than 20 fields there is nothing useful */ - if ($fcount < 20) { - $i++; - continue; - } - while ($f < $fcount) { - switch ($data[$f]) { - case "failover": - $pools[$p]['name'] = trim($data[$f+2], '"'); - $pools[$p]['name'] = "{$pools[$p]['name']} (" . convert_friendly_interface_to_friendly_descr(substr($pools[$p]['name'], 5)) . ")"; - $pools[$p]['mystate'] = $data[$f+7]; - $pools[$p]['peerstate'] = $data[$f+14]; - $pools[$p]['mydate'] = $data[$f+10]; - $pools[$p]['mydate'] .= " " . $data[$f+11]; - $pools[$p]['peerdate'] = $data[$f+17]; - $pools[$p]['peerdate'] .= " " . $data[$f+18]; - $p++; - $i++; - continue 3; - case "lease": - $leases[$l]['ip'] = $data[$f+1]; - $leases[$l]['type'] = $dynamic_string; - $f = $f+2; - break; - case "starts": - $leases[$l]['start'] = $data[$f+2]; - $leases[$l]['start'] .= " " . $data[$f+3]; - $f = $f+3; - break; - case "ends": - if ($data[$f+1] == "never") { - // Quote from dhcpd.leases(5) man page: - // If a lease will never expire, date is never instead of an actual date. - $leases[$l]['end'] = gettext("Never"); - $f = $f+1; - } else { - $leases[$l]['end'] = $data[$f+2]; - $leases[$l]['end'] .= " " . $data[$f+3]; - $f = $f+3; - } - break; - case "tstp": - $f = $f+3; - break; - case "tsfp": - $f = $f+3; - break; - case "atsfp": - $f = $f+3; - break; - case "cltt": - $f = $f+3; - break; - case "binding": - switch ($data[$f+2]) { - case "active": - $leases[$l]['act'] = $active_string; - break; - case "free": - $leases[$l]['act'] = $expired_string; - $leases[$l]['online'] = $offline_string; - break; - case "backup": - $leases[$l]['act'] = $reserved_string; - $leases[$l]['online'] = $offline_string; - break; - } - $f = $f+1; - break; - case "next": - /* skip the next binding statement */ - $f = $f+3; - break; - case "rewind": - /* skip the rewind binding statement */ - $f = $f+3; - break; - case "hardware": - $leases[$l]['mac'] = $data[$f+2]; - /* check if it's online and the lease is active */ - if (in_array($leases[$l]['ip'], $arpdata_ip)) { - $leases[$l]['online'] = $online_string; - } else { - $leases[$l]['online'] = $offline_string; - } - $f = $f+2; - break; - case "client-hostname": - if ($data[$f+1] <> "") { - $leases[$l]['hostname'] = preg_replace('/"/', '', $data[$f+1]); - } else { - $hostname = gethostbyaddr($leases[$l]['ip']); - if ($hostname <> "") { - $leases[$l]['hostname'] = $hostname; - } - } - $f = $f+1; - break; - case "uid": - $f = $f+1; - break; - } - $f++; - } - $l++; - $i++; - /* slowly chisel away at the source array */ - array_shift($leases_content); - } - /* remove duplicate items by mac address */ - if (count($leases) > 0) { - $leases = pfz_remove_duplicate($leases, "ip"); - } - - if (count($pools) > 0) { - $pools = pfz_remove_duplicate($pools, "name"); - asort($pools); - } - - switch ($valuekey){ - case "pools": - return $pools; - break; - case "failover": - return $failover; - break; - case "leases": - default: - return $leases; - } - -} - -function pfz_dhcpfailover_discovery(){ - //System functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait - require_once("system.inc"); - $leases = system_get_dhcpleases(); - - $json_string = '{"data":['; - - if (count($leases['failover']) > 0){ - foreach ($leases['failover'] as $data){ - $json_string .= '{"{#FAILOVER_GROUP}":"' . str_replace(" ", "__", $data['name']) . '"'; - } - } - - $json_string = rtrim($json_string,","); - $json_string .= "]}"; - - echo $json_string; -} - -function pfz_dhcp_check_failover() -{ - // Check DHCP Failover Status - // Returns number of failover pools which state is not normal or - // different than peer state - $failover = pfz_dhcp_get("failover"); - - return count(array_filter($failover, fn($f) => ($f["mystate"] != "normal") || ($f["mystate"] != $f["peerstate"]))); -} - -function pfz_dhcp($section, $valuekey = "") -{ - $is_known_section = array_key_exists($section, DHCP_SECTIONS); - if (!$is_known_section) { - return; - } - - DHCP_SECTIONS[$section](); -} - -// Packages -function pfz_packages_uptodate() -{ - require_once("pkg-utils.inc"); - $installed_packages = get_pkg_info("all", false, true); - - return count(array_filter( - $installed_packages, - fn($p) => $p["version"] != $p["installed_version"])); -} - -// System Information -function pfz_get_system_value($section) -{ - if ($section === "packages_update") { - echo pfz_packages_uptodate(); - return; - } - - $system_pkg_version = get_system_pkg_version(); - $version = $system_pkg_version["version"]; - $installed_version = $system_pkg_version["installed_version"]; - - if ($section === "new_version_available") { - echo pfz_bint($version != $installed_version); - return; - } - - if (array_key_exists($section, $system_pkg_version)) { - echo $system_pkg_version[$section]; - } -} - -//S.M.A.R.T Status -// Taken from /usr/local/www/widgets/widgets/smart_status.widget.php -function pfz_get_smart_status() -{ - foreach (get_smart_drive_list() as $dev) { ## for each found drive do - $dev_state = trim(exec("smartctl -H /dev/$dev | awk -F: '/^SMART overall-health self-assessment test result/ {print $2;exit} + // S.M.A.R.T Status + // Taken from /usr/local/www/widgets/widgets/smart_status.widget.php + public static function pfz_get_smart_status() + { + foreach (get_smart_drive_list() as $dev) { ## for each found drive do + $dev_state = trim(exec("smartctl -H /dev/$dev | awk -F: '/^SMART overall-health self-assessment test result/ {print $2;exit} /^SMART Health Status/ {print $2;exit}'")); ## get SMART state from drive - $is_known_state = array_key_exists($dev_state, SMART_DEV_STATUS); - if (!$is_known_state) { - return SMART_ERROR; // ED This is probably a bug, status should be echoed - } - - $status = SMART_DEV_STATUS[$dev_state]; - if ($status !== SMART_OK) { - return $status; // ED This is probably a bug, status should be echoed - } - } - - echo SMART_OK; -} - -// Certificats validity date -function pfz_get_cert_date($valuekey){ - $config = pfz_cfg(); - - $value = 0; - foreach (array("cert", "ca") as $cert_type) { - switch ($valuekey){ - case "validFrom.max": - foreach ($config[$cert_type] as $cert) { - $certinfo = openssl_x509_parse(base64_decode($cert[crt])); - if ($value == 0 or $value < $certinfo['validFrom_time_t']) $value = $certinfo['validFrom_time_t']; + $is_known_state = array_key_exists($dev_state, SMART_DEV_STATUS); + if (!$is_known_state) { + return SMART_ERROR; // ED This is probably a bug, status should be echoed } - break; - case "validTo.min": - foreach ($config[$cert_type] as $cert) { - $certinfo = openssl_x509_parse(base64_decode($cert[crt])); - if ($value == 0 or $value > $certinfo['validTo_time_t']) $value = $certinfo['validTo_time_t']; - } - break; - } - } - echo $value; + + $status = SMART_DEV_STATUS[$dev_state]; + if ($status !== SMART_OK) { + return $status; // ED This is probably a bug, status should be echoed + } + } + + echo SMART_OK; + } + + // Certificats validity date + public static function pfz_get_cert_date($valuekey) + { + $config = PfEnv::cfg(); + + $value = 0; + foreach (array("cert", "ca") as $cert_type) { + switch ($valuekey) { + case "validFrom.max": + foreach ($config[$cert_type] as $cert) { + $certinfo = openssl_x509_parse(base64_decode($cert[crt])); + if ($value == 0 or $value < $certinfo['validFrom_time_t']) $value = $certinfo['validFrom_time_t']; + } + break; + case "validTo.min": + foreach ($config[$cert_type] as $cert) { + $certinfo = openssl_x509_parse(base64_decode($cert[crt])); + if ($value == 0 or $value > $certinfo['validTo_time_t']) $value = $certinfo['validTo_time_t']; + } + break; + } + } + echo $value; + } + + // File is present + public static function pfz_file_exists($filename) + { + echo Util::b2int(file_exists($filename)); + } + + // Value mappings + // Each value map is represented by an associative array + public static function pfz_value_mapping($value_name, $value, $default_value = "0") + { + $is_known_value_name = array_key_exists($value_name, VALUE_MAPPINGS); + if (!$is_known_value_name) { + return $default_value; + } + + $value_mapping = VALUE_MAPPINGS[$value_name]; + if (!is_array($value_mapping)) { + return $default_value; + } + + $value = strtolower($value); + $is_value_with_known_mapping = array_key_exists($value, $value_mapping); + + return $is_value_with_known_mapping ? $value_mapping[$value] : $default_value; + } + + public static function pfz_discovery($section) + { + $is_known_section = in_array(strtolower($section), DISCOVERY_SECTION_HANDLERS); + if (!$is_known_section) { + return; + } + + DISCOVERY_SECTION_HANDLERS[$section](); + } } -// File is present -function pfz_file_exists($filename) { - echo pfz_bint(file_exists($filename)); -} - -// Value mappings -// Each value map is represented by an associative array -function pfz_value_mapping($value_name, $value, $default_value = "0") +function build_method_lookup(string $clazz): array { - $is_known_value_name = array_key_exists($value_name, VALUE_MAPPINGS); - if (!$is_known_value_name) { - return $default_value; - } + $all_public_methods = get_class_methods($clazz); - $value_mapping = VALUE_MAPPINGS[$value_name]; - if (!is_array($value_mapping)) { - return $default_value; - } + $just_commands = array_filter($all_public_methods, fn($name) => substr($name, 0, 3) === "pfz"); - $value = strtolower($value); - $is_value_with_known_mapping = array_key_exists($value, $value_mapping); - - return $is_value_with_known_mapping ? $value_mapping[$value] : $default_value; -} - -function pfz_discovery($section) -{ - $is_known_section = array_key_exists(strtolower($section), DISCOVERY_SECTION_HANDLERS); - if (!$is_known_section) { - return; - } - - DISCOVERY_SECTION_HANDLERS[$section](); + return array_map( + fn($command_name) => str_ireplace("pfz_", "", $command_name), + $just_commands); } function main($arguments) @@ -1291,14 +1287,14 @@ function main($arguments) $command = strtolower($arguments[1]); $parameters = array_slice($arguments, 2); - $is_known_command = array_key_exists($command, COMMAND_HANDLERS); + $is_known_command = in_array($command, COMMAND_HANDLERS); if (!$is_known_command) { - pfz_test(); + PfzCommands::pfz_test(); return; } - COMMAND_HANDLERS[$command]($parameters); + PfzCommands::{"pfz_$command"}(...$parameters); } main($argv); From 1965594c66caf8c6922d64be68937de3f600006a Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Fri, 18 Feb 2022 12:18:27 +0100 Subject: [PATCH 15/93] Wrap pfSense symbols in class --- pfsense_zbx.php | 277 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 200 insertions(+), 77 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 6db71cb..8cf31b6 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -6,21 +6,21 @@ * Written by Riccardo Bicelli * This program is licensed under Apache 2.0 License */ +require_once("config.inc"); require_once('globals.inc'); require_once('functions.inc'); -require_once('config.inc'); -require_once('util.inc'); +require_once("util.inc"); -//For Interfaces Discovery +// For Interfaces Discovery require_once('interfaces.inc'); -//For OpenVPN Discovery +// For OpenVPN Discovery require_once('openvpn.inc'); -//For Service Discovery +// For Service Discovery require_once("service-utils.inc"); -//For System +// For System require_once('pkg-utils.inc'); //Some Useful defines @@ -98,25 +98,25 @@ define('SMART_DEV_STATUS', [ define("DHCP_SECTIONS", [ "failover" => function () { - echo self::pfz_dhcp_check_failover(); + echo PfzCommands::pfz_dhcp_check_failover(); }, ]); define("OPENVPN_SERVER_VALUES", [ // Client Connections: is an array so it is sufficient to count elements "conns" => fn($server_value) => is_array($server_value) ? count($server_value) : 0, - "status" => fn($server_value) => self::pfz_value_mapping("openvpn.server.status", $server_value), - "mode" => fn($server_value) => self::pfz_value_mapping("openvpn.server.mode", $server_value) + "status" => fn($server_value) => PfzCommands::pfz_value_mapping("openvpn.server.status", $server_value), + "mode" => fn($server_value) => PfzCommands::pfz_value_mapping("openvpn.server.mode", $server_value) ]); define("IPSEC_PH1_VALUES", [ - 'status' => fn($ike_id) => self::pfz_ipsec_status($ike_id), + 'status' => fn($ike_id) => PfzCommands::pfz_ipsec_status($ike_id), 'disabled' => fn() => "0", ]); define("SERVICES_VALUES", [ "status" => function ($service) { - $status = get_service_status($service); + $status = PfEnv::get_service_status($service); return ($status == "") ? 0 : $status; }, @@ -124,22 +124,146 @@ define("SERVICES_VALUES", [ echo $name; }, "enabled" => function ($service, $name, $short_name) { - return Util::b2int(is_service_enabled($short_name)); + return Util::b2int(PfEnv::is_service_enabled($short_name)); }, "run_on_carp_slave" => function ($service, $name, $short_name, $carpcfr, $stopped_on_carp_slave) { return Util::b2int(in_array($carpcfr, $stopped_on_carp_slave)); } ]); +// Abstract undefined symbols and globals from code class PfEnv { + public const CRT = crt; + public static function cfg() { - // Abstract global variable from code global $config; return $config; } + + private static function call_pfsense_method_with_same_name_and_arguments() + { + $caller_function_name = debug_backtrace()[1]['function']; + + return call_user_func($caller_function_name, ...func_get_args()); + } + + public static function openvpn_get_active_servers() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function install_cron_job() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function openvpn_get_active_clients() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function system_get_dhcpleases() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function get_configured_interface_list() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function get_services() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function get_configured_interface_with_descr() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function get_interface_arr() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function get_interface_info() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function get_smart_drive_list() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function is_service_enabled() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function get_service_status() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function init_config_arr() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function get_ipsecifnum() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function ipsec_list_sa() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function return_gateways_status() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function get_pkg_info() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function convert_friendly_interface_to_friendly_descr() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function get_carp_interface_status() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function get_single_sysctl() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function get_carp_status() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function ipsec_ikeid_used() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function get_system_pkg_version() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } } class Util @@ -183,15 +307,15 @@ class PfzDiscoveries // Improved performance public static function pfz_interface_discovery($is_wan = false, $is_cron = false) { - $ifdescrs = get_configured_interface_with_descr(true); - $ifaces = get_interface_arr(); + $ifdescrs = PfEnv::get_configured_interface_with_descr(true); + $ifaces = PfEnv::get_interface_arr(); $ifcs = array(); $if_ret = array(); $json_string = '{"data":['; foreach ($ifdescrs as $ifname => $ifdescr) { - $ifinfo = get_interface_info($ifname); + $ifinfo = PfEnv::get_interface_info($ifname); $ifinfo["description"] = $ifdescr; $ifcs[$ifname] = $ifinfo; } @@ -232,7 +356,7 @@ class PfzDiscoveries public static function pfz_openvpn_serverdiscovery() { - $servers = self::pfz_openvpn_get_all_servers(); + $servers = PfzCommands::pfz_openvpn_get_all_servers(); $json_string = '{"data":['; @@ -252,7 +376,7 @@ class PfzDiscoveries // OpenVPN Server/User-Auth Discovery public static function pfz_openvpn_server_userdiscovery() { - $servers = self::pfz_openvpn_get_all_servers(); + $servers = PfzCommands::pfz_openvpn_get_all_servers(); $json_string = '{"data":['; @@ -283,7 +407,7 @@ class PfzDiscoveries public static function pfz_gw_discovery() { - $gws = return_gateways_status(true); + $gws = PfEnv::return_gateways_status(true); $json_string = '{"data":['; foreach ($gws as $gw) { @@ -302,7 +426,7 @@ class PfzDiscoveries require_once("ipsec.inc"); $config = PfEnv::cfg(); - init_config_arr(array('ipsec', 'phase1')); + PfEnv::init_config_arr(array('ipsec', 'phase1')); $a_phase1 = &$config['ipsec']['phase1']; $json_string = '{"data":['; @@ -326,7 +450,7 @@ class PfzDiscoveries require_once("ipsec.inc"); $config = PfEnv::cfg(); - init_config_arr(array('ipsec', 'phase2')); + PfEnv::init_config_arr(array('ipsec', 'phase2')); $a_phase2 = &$config['ipsec']['phase2']; $json_string = '{"data":['; @@ -351,7 +475,7 @@ class PfzDiscoveries { //System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait require_once("system.inc"); - $leases = system_get_dhcpleases(); + $leases = PfEnv::system_get_dhcpleases(); $json_string = '{"data":['; @@ -370,7 +494,7 @@ class PfzDiscoveries // OpenVPN Client Discovery public static function pfz_openvpn_clientdiscovery() { - $clients = openvpn_get_active_clients(); + $clients = PfEnv::openvpn_get_active_clients(); $json_string = '{"data":['; @@ -391,14 +515,14 @@ class PfzDiscoveries // 2020-03-27: Added space replace with __ for issue #12 public static function pfz_services_discovery() { - $services = get_services(); + $services = PfEnv::get_services(); $json_string = '{"data":['; foreach ($services as $service) { if (!empty($service['name'])) { - $status = get_service_status($service); + $status = PfEnv::get_service_status($service); if ($status = "") $status = 0; $id = ""; @@ -431,24 +555,24 @@ class PfzCommands print_r($ovpn_servers); echo $line; - $ovpn_clients = openvpn_get_active_clients(); + $ovpn_clients = PfEnv::openvpn_get_active_clients(); echo "OPENVPN Clients:\n"; print_r($ovpn_clients); echo $line; - $ifdescrs = self::get_configured_interface_with_descr(true); + $ifdescrs = PfEnv::get_configured_interface_with_descr(true); $ifaces = array(); foreach ($ifdescrs as $ifdescr => $ifname) { - $ifinfo = self::get_interface_info($ifdescr); + $ifinfo = PfEnv::get_interface_info($ifdescr); $ifaces[$ifname] = $ifinfo; } echo "Network Interfaces:\n"; print_r($ifaces); - print_r(self::get_interface_arr()); - print_r(self::get_configured_interface_list()); + print_r(PfEnv::get_interface_arr()); + print_r(PfEnv::get_configured_interface_list()); echo $line; - $services = self::get_services(); + $services = PfEnv::get_services(); echo "Services: \n"; print_r($services); echo $line; @@ -457,10 +581,10 @@ class PfzCommands require_once("ipsec.inc"); $config = PfEnv::cfg(); - self::init_config_arr(array('ipsec', 'phase1')); - self::init_config_arr(array('ipsec', 'phase2')); + PfEnv::init_config_arr(array('ipsec', 'phase1')); + PfEnv::init_config_arr(array('ipsec', 'phase2')); $a_phase2 = &$config['ipsec']['phase2']; - $status = self::ipsec_list_sa(); + $status = PfEnv::ipsec_list_sa(); echo "IPsec Status: \n"; print_r($status); @@ -478,7 +602,7 @@ class PfzCommands //Packages echo "Packages: \n"; require_once("pkg-utils.inc"); - $installed_packages = self::get_pkg_info('all', false, true); + $installed_packages = PfEnv::get_pkg_info('all', false, true); print_r($installed_packages); } @@ -512,8 +636,8 @@ class PfzCommands public static function pfz_speedtest_cron() { require_once("services.inc"); - $ifdescrs = get_configured_interface_with_descr(true); - $ifaces = get_interface_arr(); + $ifdescrs = PfEnv::get_configured_interface_with_descr(true); + $ifaces = PfEnv::get_interface_arr(); $pf_interface_name = ''; $subvalue = false; @@ -522,7 +646,7 @@ class PfzCommands foreach ($ifcs as $ifname) { foreach ($ifdescrs as $ifn => $ifd) { - $ifinfo = get_interface_info($ifn); + $ifinfo = PfEnv::get_interface_info($ifn); if ($ifinfo['hwif'] == $ifname) { $pf_interface_name = $ifn; break; @@ -539,7 +663,7 @@ class PfzCommands { //Install Cron Job $command = "/usr/local/bin/php " . __FILE__ . " speedtest_cron"; - install_cron_job($command, $enable, $minute = "*/15", "*", "*", "*", "*", "root", true); + PfEnv::install_cron_job($command, $enable, $minute = "*/15", "*", "*", "*", "*", "root", true); } public static function pfz_speedtest_exec($ifname, $ipaddr): bool @@ -573,8 +697,8 @@ class PfzCommands // OpenVPN Server Discovery public static function pfz_openvpn_get_all_servers() { - $servers = openvpn_get_active_servers(); - $sk_servers = openvpn_get_active_servers("p2p"); + $servers = PfEnv::openvpn_get_active_servers(); + $sk_servers = PfEnv::openvpn_get_active_servers("p2p"); $servers = array_merge($servers, $sk_servers); return ($servers); } @@ -623,7 +747,7 @@ class PfzCommands } // Get OpenVPN User Connected Value - public static function pfz_openvpn_server_uservalue($unique_id, $valuekey, $default = "") + public static function pfz_openvpn_server_uservalue($unique_id, $value_key, $default = "") { $unique_id = Util::replace_special_chars($unique_id, true); $atpos = strpos($unique_id, '+'); @@ -635,7 +759,7 @@ class PfzCommands if ($server['vpnid'] == $server_id) { foreach ($server['conns'] as $conn) { if ($conn['common_name'] == $user_id) { - $value = $conn[$valuekey]; + $value = $conn[$value_key]; } } } @@ -646,7 +770,7 @@ class PfzCommands public static function pfz_openvpn_client_value($client_id, $value_key, $fallback_value = "none") { - $clients = openvpn_get_active_clients(); + $clients = PfEnv::openvpn_get_active_clients(); $client = Util::array_first($clients, fn($client) => $client['vpnid'] == $client_id); @@ -669,7 +793,7 @@ class PfzCommands // 2020-09-28: Corrected Space Replace public static function pfz_service_value($name, $value) { - $services = get_services(); + $services = PfEnv::get_services(); $name = str_replace("__", " ", $name); // List of service which are stopped on CARP Slave. @@ -701,12 +825,12 @@ class PfzCommands } } - public static function pfz_gw_value($gw, $valuekey) + public static function pfz_gw_value($gw, $value_key) { - $gws = return_gateways_status(true); + $gws = PfEnv::return_gateways_status(true); if (array_key_exists($gw, $gws)) { - $value = $gws[$gw][$valuekey]; - if ($valuekey == "status") { + $value = $gws[$gw][$value_key]; + if ($value_key == "status") { //Issue #70: Gateway Forced Down if ($gws[$gw]["substatus"] <> "none") $value = $gws[$gw]["substatus"]; @@ -723,7 +847,7 @@ class PfzCommands // If Getting "disabled" value only check item presence in config array require_once("ipsec.inc"); $config = PfEnv::cfg(); - init_config_arr(array('ipsec', 'phase1')); + PfEnv::init_config_arr(array('ipsec', 'phase1')); $a_phase1 = &$config['ipsec']['phase1']; $is_known_ipsec_key = array_key_exists($value_key, IPSEC_PH1_VALUES); @@ -751,14 +875,14 @@ class PfzCommands echo self::pfz_value_mapping("ipsec." . $value_key, $maybe_ike_match[$value_key]); } - public static function pfz_ipsec_ph2($uniqid, $valuekey) + public static function pfz_ipsec_ph2($uniqid, $value_key) { require_once("ipsec.inc"); $config = PfEnv::cfg(); - init_config_arr(array('ipsec', 'phase2')); + PfEnv::init_config_arr(array('ipsec', 'phase2')); $a_phase2 = &$config['ipsec']['phase2']; - $valuecfr = explode(".", $valuekey); + $valuecfr = explode(".", $value_key); switch ($valuecfr[0]) { case 'status': @@ -773,11 +897,11 @@ class PfzCommands foreach ($a_phase2 as $data) { if ($data['uniqid'] == $uniqid) { - if (array_key_exists($valuekey, $data)) { - if ($valuekey == 'disabled') + if (array_key_exists($value_key, $data)) { + if ($value_key == 'disabled') $value = "1"; else - $value = self::pfz_value_mapping("ipsec_ph2." . $valuekey, $data[$valuekey], $data[$valuekey]); + $value = self::pfz_value_mapping("ipsec_ph2." . $value_key, $data[$value_key], $data[$value_key]); break; } } @@ -790,14 +914,14 @@ class PfzCommands require_once("ipsec.inc"); $config = PfEnv::cfg(); - init_config_arr(array('ipsec', 'phase1')); + PfEnv::init_config_arr(array('ipsec', 'phase1')); $a_phase1 = &$config['ipsec']['phase1']; $conmap = array(); foreach ($a_phase1 as $ph1ent) { if (function_exists('get_ipsecifnum')) { - if (get_ipsecifnum($ph1ent['ikeid'], 0)) { - $cname = "con" . get_ipsecifnum($ph1ent['ikeid'], 0); + if (PfEnv::get_ipsecifnum($ph1ent['ikeid'], 0)) { + $cname = "con" . PfEnv::get_ipsecifnum($ph1ent['ikeid'], 0); } else { $cname = "con{$ph1ent['ikeid']}00000"; } @@ -808,7 +932,7 @@ class PfzCommands $conmap[$cname] = $ph1ent['ikeid']; } - $status = ipsec_list_sa(); + $status = PfEnv::ipsec_list_sa(); $ipsecconnected = array(); $carp_status = self::pfz_carp_status(false); @@ -827,7 +951,7 @@ class PfzCommands $ph1idx = $conmap[$con_name]; $ipsecconnected[$ph1idx] = $ph1idx; } else { - if (!ipsec_ikeid_used($con_id)) { + if (!PfEnv::ipsec_ikeid_used($con_id)) { // probably a v2 with split connection then $ph1idx = $conmap[$con_name]; $ipsecconnected[$ph1idx] = $ph1idx; @@ -909,8 +1033,8 @@ class PfzCommands //Detect CARP Status $config = PfEnv::cfg(); $status_return = 0; - $status = get_carp_status(); - $carp_detected_problems = get_single_sysctl("net.inet.carp.demotion"); + $status = PfEnv::get_carp_status(); + $carp_detected_problems = PfEnv::get_single_sysctl("net.inet.carp.demotion"); //CARP is disabled $ret = 0; @@ -930,7 +1054,7 @@ class PfzCommands if ($carp['mode'] != "carp") { continue; } - $if_status = get_carp_interface_status("_vip{$carp['uniqid']}"); + $if_status = PfEnv::get_carp_interface_status("_vip{$carp['uniqid']}"); if (($prev_status != $if_status) && (empty($if_status) == false)) { //Some glitches with GUI if ($prev_status != "") $status_changed = true; @@ -968,10 +1092,9 @@ class PfzCommands } // Get DHCP Arrays (copied from status_dhcp_leases.php, waiting for pfsense 2.5, in order to use system_get_dhcpleases();) - public static function pfz_dhcp_get($valuekey) + public static function pfz_dhcp_get($value_key) { - require_once("config.inc"); $leasesfile = "{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases"; @@ -1001,7 +1124,7 @@ class PfzCommands switch ($data[$f]) { case "failover": $pools[$p]['name'] = trim($data[$f + 2], '"'); - $pools[$p]['name'] = "{$pools[$p]['name']} (" . convert_friendly_interface_to_friendly_descr(substr($pools[$p]['name'], 5)) . ")"; + $pools[$p]['name'] = "{$pools[$p]['name']} (" . PfEnv::convert_friendly_interface_to_friendly_descr(substr($pools[$p]['name'], 5)) . ")"; $pools[$p]['mystate'] = $data[$f + 7]; $pools[$p]['peerstate'] = $data[$f + 14]; $pools[$p]['mydate'] = $data[$f + 10]; @@ -1111,7 +1234,7 @@ class PfzCommands asort($pools); } - switch ($valuekey) { + switch ($value_key) { case "pools": return $pools; break; @@ -1131,7 +1254,7 @@ class PfzCommands echo implode(",", array_map( fn($gw) => sprintf("%s.%s", $gw['name'], $gw['status']), - self::return_gateways_status(true))); + PfEnv::return_gateways_status(true))); } public static function pfz_dhcp_check_failover() @@ -1144,7 +1267,7 @@ class PfzCommands return count(array_filter($failover, fn($f) => ($f["mystate"] != "normal") || ($f["mystate"] != $f["peerstate"]))); } - public static function pfz_dhcp($section, $valuekey = "") + public static function pfz_dhcp($section, $value_key = "") { $is_known_section = array_key_exists($section, DHCP_SECTIONS); if (!$is_known_section) { @@ -1158,7 +1281,7 @@ class PfzCommands public static function pfz_packages_uptodate() { require_once("pkg-utils.inc"); - $installed_packages = self::get_pkg_info("all", false, true); + $installed_packages = PfEnv::get_pkg_info("all", false, true); return count(array_filter( @@ -1174,7 +1297,7 @@ class PfzCommands return; } - $system_pkg_version = self::get_system_pkg_version(); + $system_pkg_version = PfEnv::get_system_pkg_version(); $version = $system_pkg_version["version"]; $installed_version = $system_pkg_version["installed_version"]; @@ -1192,7 +1315,7 @@ class PfzCommands // Taken from /usr/local/www/widgets/widgets/smart_status.widget.php public static function pfz_get_smart_status() { - foreach (get_smart_drive_list() as $dev) { ## for each found drive do + foreach (PfEnv::get_smart_drive_list() as $dev) { ## for each found drive do $dev_state = trim(exec("smartctl -H /dev/$dev | awk -F: '/^SMART overall-health self-assessment test result/ {print $2;exit} /^SMART Health Status/ {print $2;exit}'")); ## get SMART state from drive $is_known_state = array_key_exists($dev_state, SMART_DEV_STATUS); @@ -1210,22 +1333,22 @@ class PfzCommands } // Certificats validity date - public static function pfz_get_cert_date($valuekey) + public static function pfz_get_cert_date($value_key) { $config = PfEnv::cfg(); $value = 0; foreach (array("cert", "ca") as $cert_type) { - switch ($valuekey) { + switch ($value_key) { case "validFrom.max": foreach ($config[$cert_type] as $cert) { - $certinfo = openssl_x509_parse(base64_decode($cert[crt])); + $certinfo = openssl_x509_parse(base64_decode($cert[PfEnv::CRT])); if ($value == 0 or $value < $certinfo['validFrom_time_t']) $value = $certinfo['validFrom_time_t']; } break; case "validTo.min": foreach ($config[$cert_type] as $cert) { - $certinfo = openssl_x509_parse(base64_decode($cert[crt])); + $certinfo = openssl_x509_parse(base64_decode($cert[PfEnv::CRT])); if ($value == 0 or $value > $certinfo['validTo_time_t']) $value = $certinfo['validTo_time_t']; } break; From 718fcfdf8bcb72dcddef2dd1f676982b2089e8b4 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Fri, 18 Feb 2022 12:22:09 +0100 Subject: [PATCH 16/93] Add help command --- pfsense_zbx.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 8cf31b6..98e0f9f 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1410,11 +1410,17 @@ function main($arguments) $command = strtolower($arguments[1]); $parameters = array_slice($arguments, 2); + + if ($command == "help") { + print_r(COMMAND_HANDLERS); + exit; + } + $is_known_command = in_array($command, COMMAND_HANDLERS); if (!$is_known_command) { PfzCommands::pfz_test(); - return; + exit; } PfzCommands::{"pfz_$command"}(...$parameters); From c25675834ed53c4846e96520a52b1e1b0a0b6d79 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Fri, 18 Feb 2022 12:58:53 +0100 Subject: [PATCH 17/93] Use correct method names and hide private --- pfsense_zbx.php | 1022 ++++++++++++++++++++++++----------------------- 1 file changed, 517 insertions(+), 505 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 98e0f9f..f5bedf6 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -96,23 +96,6 @@ define('SMART_DEV_STATUS', [ SMART_DEV_UNKNOWN => SMART_UNKNOWN ]); -define("DHCP_SECTIONS", [ - "failover" => function () { - echo PfzCommands::pfz_dhcp_check_failover(); - }, -]); - -define("OPENVPN_SERVER_VALUES", [ - // Client Connections: is an array so it is sufficient to count elements - "conns" => fn($server_value) => is_array($server_value) ? count($server_value) : 0, - "status" => fn($server_value) => PfzCommands::pfz_value_mapping("openvpn.server.status", $server_value), - "mode" => fn($server_value) => PfzCommands::pfz_value_mapping("openvpn.server.mode", $server_value) -]); - -define("IPSEC_PH1_VALUES", [ - 'status' => fn($ike_id) => PfzCommands::pfz_ipsec_status($ike_id), - 'disabled' => fn() => "0", -]); define("SERVICES_VALUES", [ "status" => function ($service) { @@ -303,9 +286,24 @@ class Util class PfzDiscoveries { + public static function pfz_gw() + { + $gws = PfEnv::return_gateways_status(true); + + $json_string = '{"data":['; + foreach ($gws as $gw) { + $json_string .= '{"{#GATEWAY}":"' . $gw['name'] . '"'; + $json_string .= '},'; + } + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + } + // Interface Discovery // Improved performance - public static function pfz_interface_discovery($is_wan = false, $is_cron = false) + private static function discover_interface($is_wan = false, $is_cron = false) { $ifdescrs = PfEnv::get_configured_interface_with_descr(true); $ifaces = PfEnv::get_interface_arr(); @@ -354,9 +352,14 @@ class PfzDiscoveries echo $json_string; } - public static function pfz_openvpn_serverdiscovery() + public static function pfz_wan($is_wan = false, $is_cron = false) { - $servers = PfzCommands::pfz_openvpn_get_all_servers(); + self::discover_interface(true); + } + + public static function pfz_openvpn_server() + { + $servers = PfzOpenVpn::get_all_openvpn_servers(); $json_string = '{"data":['; @@ -374,9 +377,9 @@ class PfzDiscoveries } // OpenVPN Server/User-Auth Discovery - public static function pfz_openvpn_server_userdiscovery() + public static function pfz_openvpn_server_user() { - $servers = PfzCommands::pfz_openvpn_get_all_servers(); + $servers = PfzOpenVpn::get_all_openvpn_servers(); $json_string = '{"data":['; @@ -405,94 +408,8 @@ class PfzDiscoveries echo $json_string; } - public static function pfz_gw_discovery() - { - $gws = PfEnv::return_gateways_status(true); - - $json_string = '{"data":['; - foreach ($gws as $gw) { - $json_string .= '{"{#GATEWAY}":"' . $gw['name'] . '"'; - $json_string .= '},'; - } - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; - } - - // IPSEC Discovery - public static function pfz_ipsec_discovery_ph1() - { - - require_once("ipsec.inc"); - $config = PfEnv::cfg(); - PfEnv::init_config_arr(array('ipsec', 'phase1')); - $a_phase1 = &$config['ipsec']['phase1']; - - $json_string = '{"data":['; - - foreach ($a_phase1 as $data) { - $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; - $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; - $json_string .= '},'; - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; - - } - - public static function pfz_ipsec_discovery_ph2() - { - - require_once("ipsec.inc"); - - $config = PfEnv::cfg(); - PfEnv::init_config_arr(array('ipsec', 'phase2')); - $a_phase2 = &$config['ipsec']['phase2']; - - $json_string = '{"data":['; - - foreach ($a_phase2 as $data) { - $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; - $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; - $json_string .= ',"{#UNIQID}":"' . $data['uniqid'] . '"'; - $json_string .= ',"{#REQID}":"' . $data['reqid'] . '"'; - $json_string .= ',"{#EXTID}":"' . $data['ikeid'] . '.' . $data['reqid'] . '"'; - $json_string .= '},'; - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; - - } - - public static function pfz_dhcpfailover_discovery() - { - //System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait - require_once("system.inc"); - $leases = PfEnv::system_get_dhcpleases(); - - $json_string = '{"data":['; - - if (count($leases['failover']) > 0) { - foreach ($leases['failover'] as $data) { - $json_string .= '{"{#FAILOVER_GROUP}":"' . str_replace(" ", "__", $data['name']) . '"'; - } - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; - } - // OpenVPN Client Discovery - public static function pfz_openvpn_clientdiscovery() + public static function pfz_openvpn_client() { $clients = PfEnv::openvpn_get_active_clients(); @@ -541,73 +458,87 @@ class PfzDiscoveries echo $json_string; } -} -class PfzCommands -{ - // Testing function, for template creating purpose - public static function pfz_test() + public static function pfz_interfaces($is_wan = false, $is_cron = false) { - $line = "-------------------\n"; + self::discover_interface(); + } - $ovpn_servers = self::pfz_openvpn_get_all_servers(); - echo "OPENVPN Servers:\n"; - print_r($ovpn_servers); - echo $line; - - $ovpn_clients = PfEnv::openvpn_get_active_clients(); - echo "OPENVPN Clients:\n"; - print_r($ovpn_clients); - echo $line; - - $ifdescrs = PfEnv::get_configured_interface_with_descr(true); - $ifaces = array(); - foreach ($ifdescrs as $ifdescr => $ifname) { - $ifinfo = PfEnv::get_interface_info($ifdescr); - $ifaces[$ifname] = $ifinfo; - } - echo "Network Interfaces:\n"; - print_r($ifaces); - print_r(PfEnv::get_interface_arr()); - print_r(PfEnv::get_configured_interface_list()); - echo $line; - - $services = PfEnv::get_services(); - echo "Services: \n"; - print_r($services); - echo $line; - - echo "IPsec: \n"; + // IPSEC Discovery + public static function pfz_ipsec_ph1() + { require_once("ipsec.inc"); $config = PfEnv::cfg(); PfEnv::init_config_arr(array('ipsec', 'phase1')); - PfEnv::init_config_arr(array('ipsec', 'phase2')); - $a_phase2 = &$config['ipsec']['phase2']; - $status = PfEnv::ipsec_list_sa(); - echo "IPsec Status: \n"; - print_r($status); - $a_phase1 = &$config['ipsec']['phase1']; - $a_phase2 = &$config['ipsec']['phase2']; - echo "IPsec Config Phase 1: \n"; - print_r($a_phase1); + $json_string = '{"data":['; - echo "IPsec Config Phase 2: \n"; - print_r($a_phase2); + foreach ($a_phase1 as $data) { + $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; + $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; + $json_string .= '},'; + } - echo $line; + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; - //Packages - echo "Packages: \n"; - require_once("pkg-utils.inc"); - $installed_packages = PfEnv::get_pkg_info('all', false, true); - print_r($installed_packages); } + public static function pfz_ipsec_ph2() + { + require_once("ipsec.inc"); + + $config = PfEnv::cfg(); + PfEnv::init_config_arr(array('ipsec', 'phase2')); + $a_phase2 = &$config['ipsec']['phase2']; + + $json_string = '{"data":['; + + foreach ($a_phase2 as $data) { + $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; + $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; + $json_string .= ',"{#UNIQID}":"' . $data['uniqid'] . '"'; + $json_string .= ',"{#REQID}":"' . $data['reqid'] . '"'; + $json_string .= ',"{#EXTID}":"' . $data['ikeid'] . '.' . $data['reqid'] . '"'; + $json_string .= '},'; + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + + } + + public static function pfz_dhcpfailover() + { + //System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait + require_once("system.inc"); + $leases = PfEnv::system_get_dhcpleases(); + + $json_string = '{"data":['; + + if (count($leases['failover']) > 0) { + foreach ($leases['failover'] as $data) { + $json_string .= '{"{#FAILOVER_GROUP}":"' . str_replace(" ", "__", $data['name']) . '"'; + } + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + } +} + +class PfzSpeedtest +{ // Interface Speedtest - public static function pfz_interface_speedtest_value($ifname, $value) + public static function pfz_interface_speedtest_value($if_name, $value) { $tvalue = explode(".", $value); @@ -617,7 +548,7 @@ class PfzCommands } //If the interface has a gateway is considered WAN, so let's do the speedtest - $filename = "/tmp/speedtest-$ifname"; + $filename = "/tmp/speedtest-$if_name"; if (file_exists($filename)) { $speedtest_data = json_decode(file_get_contents($filename), true); @@ -629,34 +560,8 @@ class PfzCommands echo $speedtest_data[$value][$subvalue]; } } - } - // This is supposed to run via cron job - public static function pfz_speedtest_cron() - { - require_once("services.inc"); - $ifdescrs = PfEnv::get_configured_interface_with_descr(true); - $ifaces = PfEnv::get_interface_arr(); - $pf_interface_name = ''; - $subvalue = false; - - $ifcs = PfzDiscoveries::pfz_interface_discovery(true, true); - - foreach ($ifcs as $ifname) { - - foreach ($ifdescrs as $ifn => $ifd) { - $ifinfo = PfEnv::get_interface_info($ifn); - if ($ifinfo['hwif'] == $ifname) { - $pf_interface_name = $ifn; - break; - } - } - - self::pfz_speedtest_exec($ifname, $ifinfo['ipaddr']); - - } - } // Installs a cron job for speedtests public static function pfz_speedtest_cron_install($enable = true) @@ -666,10 +571,10 @@ class PfzCommands PfEnv::install_cron_job($command, $enable, $minute = "*/15", "*", "*", "*", "*", "root", true); } - public static function pfz_speedtest_exec($ifname, $ipaddr): bool + public static function pfz_speedtest_exec($if_name, $ip_address): bool { - $filename = "/tmp/speedtest-$ifname"; + $filename = "/tmp/speedtest-$if_name"; $filetemp = "$filename.tmp"; $filerun = "/tmp/speedtest-run"; @@ -683,7 +588,7 @@ class PfzCommands if (file_exists($filerun) == false) { touch($filerun); - $st_command = "/usr/local/bin/speedtest --source $ipaddr --json > $filetemp"; + $st_command = "/usr/local/bin/speedtest --source $ip_address --json > $filetemp"; exec($st_command); rename($filetemp, $filename); @unlink($filerun); @@ -692,69 +597,90 @@ class PfzCommands return true; } +} - - // OpenVPN Server Discovery - public static function pfz_openvpn_get_all_servers() +class PfzOpenVpn +{ + public static function get_all_openvpn_servers() { $servers = PfEnv::openvpn_get_active_servers(); $sk_servers = PfEnv::openvpn_get_active_servers("p2p"); $servers = array_merge($servers, $sk_servers); return ($servers); } +} - public static function pfz_retrieve_server_value($maybe_server, $value_key) +class PfzCommands +{ + public static function pfz_discovery($section) { - if (empty($maybe_server)) { - return null; + $is_known_section = in_array(strtolower($section), DISCOVERY_SECTION_HANDLERS); + if (!$is_known_section) { + return; } - $raw_value = $maybe_server[$value_key]; - - if (in_array($maybe_server["mode"], ["server_user", "server_tls_user", "server_tls"])) { - return $raw_value == "" ? "server_user_listening" : $raw_value; - } - - if ($maybe_server["mode"] == "p2p_tls") { - // For p2p_tls, ensure we have one client, and return up if it's the case - if ($raw_value == "") { - $has_at_least_one_connection = - is_array($maybe_server["conns"]) && count($maybe_server["conns"]) > 0; - - return $has_at_least_one_connection ? "up" : "down"; - } - } - - return $raw_value; + DISCOVERY_SECTION_HANDLERS[$section](); } - // Get OpenVPN Server Value - public static function pfz_openvpn_server_value($server_id, $value_key) + public static function pfz_gw_value($gw, $value_key) { - $servers = self::pfz_openvpn_get_all_servers(); + $gws = PfEnv::return_gateways_status(true); + if (array_key_exists($gw, $gws)) { + $value = $gws[$gw][$value_key]; + if ($value_key == "status") { + //Issue #70: Gateway Forced Down + if ($gws[$gw]["substatus"] <> "none") + $value = $gws[$gw]["substatus"]; + + $value = self::pfz_value_mapping("gateway.status", $value); + } + echo $value; + } + } + + public static function pfz_gw_status() + { + echo implode(",", + array_map( + fn($gw) => sprintf("%s.%s", $gw['name'], $gw['status']), + PfEnv::return_gateways_status(true))); + } + + public static function pfz_if_speedtest_value($if_name, $value) + { + PfzSpeedtest::pfz_speedtest_cron_install(); + PfzSpeedtest::pfz_interface_speedtest_value($if_name, $value); + } + + public static function pfz_openvpn_servervalue($server_id, $value_key) + { + $servers = self::get_all_openvpn_servers(); $maybe_server = Util::array_first($servers, fn($s) => $s['vpnid'] == $server_id); - $server_value = self::pfz_retrieve_server_value($maybe_server, $value_key); + $server_value = self::get_server_value($maybe_server, $value_key); - $is_known_value_key = array_key_exists($value_key, OPENVPN_SERVER_VALUES); - if ($is_known_value_key) { - echo OPENVPN_SERVER_VALUES[$value_key]($server_value); + if ($value_key == "conns") { + echo is_array($server_value) ? count($server_value) : 0; + return; + } + + if (in_array($value_key, ["status", "mode"])) { + echo PfzCommands::pfz_value_mapping("openvpn.server.status", $server_value); return; } echo $server_value; } - // Get OpenVPN User Connected Value - public static function pfz_openvpn_server_uservalue($unique_id, $value_key, $default = "") + private static function pfz_openvpn_server_uservalue_($unique_id, $value_key, $default = "") { $unique_id = Util::replace_special_chars($unique_id, true); $atpos = strpos($unique_id, '+'); $server_id = substr($unique_id, 0, $atpos); $user_id = substr($unique_id, $atpos + 1); - $servers = self::pfz_openvpn_get_all_servers(); + $servers = PfzOpenVpn::get_all_openvpn_servers(); foreach ($servers as $server) { if ($server['vpnid'] == $server_id) { foreach ($server['conns'] as $conn) { @@ -764,11 +690,22 @@ class PfzCommands } } } - if ($value == "") $value = $default; - echo $value; + + return ($value == "") ? $default : $value; } - public static function pfz_openvpn_client_value($client_id, $value_key, $fallback_value = "none") + public static function pfz_openvpn_server_uservalue($unique_id, $value_key) + { + return self::pfz_openvpn_server_uservalue_($unique_id, $value_key); + } + + public static function pfz_openvpn_server_uservalue_numeric($unique_id, $value_key) + { + return self::pfz_openvpn_server_uservalue_($unique_id, $value_key, "0"); + } + + + public static function pfz_openvpn_clientvalue($client_id, $value_key, $fallback_value = "none") { $clients = PfEnv::openvpn_get_active_clients(); @@ -788,9 +725,6 @@ class PfzCommands return ($maybe_value == "") ? $fallback_value : $maybe_value; } - // Get service value - // 2020-03-27: Added space replace in service name for issue #12 - // 2020-09-28: Corrected Space Replace public static function pfz_service_value($name, $value) { $services = PfEnv::get_services(); @@ -825,172 +759,6 @@ class PfzCommands } } - public static function pfz_gw_value($gw, $value_key) - { - $gws = PfEnv::return_gateways_status(true); - if (array_key_exists($gw, $gws)) { - $value = $gws[$gw][$value_key]; - if ($value_key == "status") { - //Issue #70: Gateway Forced Down - if ($gws[$gw]["substatus"] <> "none") - $value = $gws[$gw]["substatus"]; - - $value = self::pfz_value_mapping("gateway.status", $value); - } - echo $value; - } - } - - public static function pfz_ipsec_ph1($ike_id, $value_key) - { - // Get Value from IPsec Phase 1 Configuration - // If Getting "disabled" value only check item presence in config array - require_once("ipsec.inc"); - $config = PfEnv::cfg(); - PfEnv::init_config_arr(array('ipsec', 'phase1')); - $a_phase1 = &$config['ipsec']['phase1']; - - $is_known_ipsec_key = array_key_exists($value_key, IPSEC_PH1_VALUES); - if ($is_known_ipsec_key) { - echo IPSEC_PH1_VALUES[$value_key]($ike_id); - return; - } - - $maybe_ike_match = Util::array_first($a_phase1, fn($d) => $d["ikeid"] == $ike_id); - if (empty($maybe_ike_match)) { - echo ""; - return; - } - - if (!array_key_exists($value_key, $maybe_ike_match)) { - echo ""; - return; - } - - if ($value_key == 'disabled') { - echo "1"; - return; - } - - echo self::pfz_value_mapping("ipsec." . $value_key, $maybe_ike_match[$value_key]); - } - - public static function pfz_ipsec_ph2($uniqid, $value_key) - { - require_once("ipsec.inc"); - $config = PfEnv::cfg(); - PfEnv::init_config_arr(array('ipsec', 'phase2')); - $a_phase2 = &$config['ipsec']['phase2']; - - $valuecfr = explode(".", $value_key); - - switch ($valuecfr[0]) { - case 'status': - $idarr = explode(".", $uniqid); - $statuskey = "state"; - if (isset($valuecfr[1])) $statuskey = $valuecfr[1]; - $value = self::pfz_ipsec_status($idarr[0], $idarr[1], $statuskey); - break; - case 'disabled': - $value = "0"; - } - - foreach ($a_phase2 as $data) { - if ($data['uniqid'] == $uniqid) { - if (array_key_exists($value_key, $data)) { - if ($value_key == 'disabled') - $value = "1"; - else - $value = self::pfz_value_mapping("ipsec_ph2." . $value_key, $data[$value_key], $data[$value_key]); - break; - } - } - } - echo $value; - } - - public static function pfz_ipsec_status($ike_id, $req_id = -1, $value_key = 'state') - { - - require_once("ipsec.inc"); - $config = PfEnv::cfg(); - PfEnv::init_config_arr(array('ipsec', 'phase1')); - - $a_phase1 = &$config['ipsec']['phase1']; - $conmap = array(); - foreach ($a_phase1 as $ph1ent) { - if (function_exists('get_ipsecifnum')) { - if (PfEnv::get_ipsecifnum($ph1ent['ikeid'], 0)) { - $cname = "con" . PfEnv::get_ipsecifnum($ph1ent['ikeid'], 0); - } else { - $cname = "con{$ph1ent['ikeid']}00000"; - } - } else { - $cname = ipsec_conid($ph1ent); - } - - $conmap[$cname] = $ph1ent['ikeid']; - } - - $status = PfEnv::ipsec_list_sa(); - $ipsecconnected = array(); - - $carp_status = self::pfz_carp_status(false); - - //Phase-Status match borrowed from status_ipsec.php - if (is_array($status)) { - foreach ($status as $l_ikeid => $ikesa) { - - if (isset($ikesa['con-id'])) { - $con_id = substr($ikesa['con-id'], 3); - } else { - $con_id = filter_var($ike_id, FILTER_SANITIZE_NUMBER_INT); - } - $con_name = "con" . $con_id; - if ($ikesa['version'] == 1) { - $ph1idx = $conmap[$con_name]; - $ipsecconnected[$ph1idx] = $ph1idx; - } else { - if (!PfEnv::ipsec_ikeid_used($con_id)) { - // probably a v2 with split connection then - $ph1idx = $conmap[$con_name]; - $ipsecconnected[$ph1idx] = $ph1idx; - } else { - $ipsecconnected[$con_id] = $ph1idx = $con_id; - } - } - if ($ph1idx == $ike_id) { - if ($req_id != -1) { - // Asking for Phase2 Status Value - foreach ($ikesa['child-sas'] as $childsas) { - if ($childsas['reqid'] == $req_id) { - if (strtolower($childsas['state']) == 'rekeyed') { - //if state is rekeyed go on - $tmp_value = $childsas[$value_key]; - } else { - $tmp_value = $childsas[$value_key]; - break; - } - } - } - } else { - $tmp_value = $ikesa[$value_key]; - } - - break; - } - } - } - - if ($value_key == "state") { - $v = self::pfz_value_mapping('ipsec.state', strtolower($tmp_value)); - - return ($carp_status != 0) ? $v + (10 * ($carp_status - 1)) : $v; - } - - return $tmp_value; - } - function pfz_temperature_sensors_discovery() { @@ -1078,8 +846,354 @@ class PfzCommands } + // System Information + public static function pfz_get_system_value($section) + { + if ($section === "packages_update") { + echo self::get_outdated_packages(); + return; + } + + $system_pkg_version = PfEnv::get_system_pkg_version(); + $version = $system_pkg_version["version"]; + $installed_version = $system_pkg_version["installed_version"]; + + if ($section === "new_version_available") { + echo Util::b2int($version != $installed_version); + return; + } + + if (array_key_exists($section, $system_pkg_version)) { + echo $system_pkg_version[$section]; + } + } + + public static function pfz_ipsec_ph1($ike_id, $value_key) + { + // Get Value from IPsec Phase 1 Configuration + // If Getting "disabled" value only check item presence in config array + require_once("ipsec.inc"); + $config = PfEnv::cfg(); + PfEnv::init_config_arr(array('ipsec', 'phase1')); + $a_phase1 = &$config['ipsec']['phase1']; + + if ($value_key == "status") { + echo PfzCommands::get_ipsec_status($ike_id); + return; + } + + if ($value_key == "disabled") { + echo "0"; + return; + } + + $maybe_ike_match = Util::array_first($a_phase1, fn($d) => $d["ikeid"] == $ike_id); + if (empty($maybe_ike_match)) { + echo ""; + return; + } + + if (!array_key_exists($value_key, $maybe_ike_match)) { + echo ""; + return; + } + + echo self::pfz_value_mapping("ipsec." . $value_key, $maybe_ike_match[$value_key]); + } + + public static function pfz_ipsec_ph2($uniqid, $value_key) + { + require_once("ipsec.inc"); + $config = PfEnv::cfg(); + PfEnv::init_config_arr(array('ipsec', 'phase2')); + $a_phase2 = &$config['ipsec']['phase2']; + + $valuecfr = explode(".", $value_key); + + switch ($valuecfr[0]) { + case 'status': + $idarr = explode(".", $uniqid); + $statuskey = "state"; + if (isset($valuecfr[1])) $statuskey = $valuecfr[1]; + $value = self::get_ipsec_status($idarr[0], $idarr[1], $statuskey); + break; + case 'disabled': + $value = "0"; + } + + foreach ($a_phase2 as $data) { + if ($data['uniqid'] == $uniqid) { + if (array_key_exists($value_key, $data)) { + if ($value_key == 'disabled') + $value = "1"; + else + $value = self::pfz_value_mapping("ipsec_ph2." . $value_key, $data[$value_key], $data[$value_key]); + break; + } + } + } + echo $value; + } + + public static function pfz_dhcp($section) + { + if ($section != "failover") { + return; + } + + echo PfzCommands::check_dhcp_failover(); + } + + // File is present + public static function pfz_file_exists($filename) + { + echo Util::b2int(file_exists($filename)); + } + + public static function pfz_speedtest_cron() + { + require_once("services.inc"); + $ifdescrs = PfEnv::get_configured_interface_with_descr(true); + $ifaces = PfEnv::get_interface_arr(); + $pf_interface_name = ''; + $subvalue = false; + + $ifcs = PfzDiscoveries::pfz_interface_discovery(true, true); + + foreach ($ifcs as $if_name) { + + foreach ($ifdescrs as $ifn => $ifd) { + $if_info = PfEnv::get_interface_info($ifn); + if ($if_info['hwif'] == $if_name) { + $pf_interface_name = $ifn; + break; + } + } + + PfzSpeedtest::pfz_speedtest_exec($if_name, $if_info['ipaddr']); + } + } + + public static function pfz_cron_cleanup() + { + PfzSpeedtest::pfz_speedtest_cron_install(false); + } + + // S.M.A.R.T Status + // Taken from /usr/local/www/widgets/widgets/smart_status.widget.php + public static function pfz_smart_status() + { + foreach (PfEnv::get_smart_drive_list() as $dev) { ## for each found drive do + $dev_state = trim(exec("smartctl -H /dev/$dev | awk -F: '/^SMART overall-health self-assessment test result/ {print $2;exit} +/^SMART Health Status/ {print $2;exit}'")); ## get SMART state from drive + $is_known_state = array_key_exists($dev_state, SMART_DEV_STATUS); + if (!$is_known_state) { + return SMART_ERROR; // ED This is probably a bug, status should be echoed + } + + $status = SMART_DEV_STATUS[$dev_state]; + if ($status !== SMART_OK) { + return $status; // ED This is probably a bug, status should be echoed + } + } + + echo SMART_OK; + } + + public static function pfz_cert_date($value_key) + { + $config = PfEnv::cfg(); + + $value = 0; + foreach (array("cert", "ca") as $cert_type) { + switch ($value_key) { + case "validFrom.max": + foreach ($config[$cert_type] as $cert) { + $certinfo = openssl_x509_parse(base64_decode($cert[PfEnv::CRT])); + if ($value == 0 or $value < $certinfo['validFrom_time_t']) $value = $certinfo['validFrom_time_t']; + } + break; + case "validTo.min": + foreach ($config[$cert_type] as $cert) { + $certinfo = openssl_x509_parse(base64_decode($cert[PfEnv::CRT])); + if ($value == 0 or $value > $certinfo['validTo_time_t']) $value = $certinfo['validTo_time_t']; + } + break; + } + } + echo $value; + } + + // Testing function, for template creating purpose + public static function pfz_test() + { + $line = "-------------------\n"; + + $ovpn_servers = PfzOpenVpn::get_all_openvpn_servers(); + echo "OPENVPN Servers:\n"; + print_r($ovpn_servers); + echo $line; + + $ovpn_clients = PfEnv::openvpn_get_active_clients(); + echo "OPENVPN Clients:\n"; + print_r($ovpn_clients); + echo $line; + + $ifdescrs = PfEnv::get_configured_interface_with_descr(true); + $ifaces = array(); + foreach ($ifdescrs as $ifdescr => $ifname) { + $ifinfo = PfEnv::get_interface_info($ifdescr); + $ifaces[$ifname] = $ifinfo; + } + echo "Network Interfaces:\n"; + print_r($ifaces); + print_r(PfEnv::get_interface_arr()); + print_r(PfEnv::get_configured_interface_list()); + echo $line; + + $services = PfEnv::get_services(); + echo "Services: \n"; + print_r($services); + echo $line; + + echo "IPsec: \n"; + + require_once("ipsec.inc"); + $config = PfEnv::cfg(); + PfEnv::init_config_arr(array('ipsec', 'phase1')); + PfEnv::init_config_arr(array('ipsec', 'phase2')); + $a_phase2 = &$config['ipsec']['phase2']; + $status = PfEnv::ipsec_list_sa(); + echo "IPsec Status: \n"; + print_r($status); + + $a_phase1 = &$config['ipsec']['phase1']; + $a_phase2 = &$config['ipsec']['phase2']; + + echo "IPsec Config Phase 1: \n"; + print_r($a_phase1); + + echo "IPsec Config Phase 2: \n"; + print_r($a_phase2); + + echo $line; + + //Packages + echo "Packages: \n"; + require_once("pkg-utils.inc"); + $installed_packages = PfEnv::get_pkg_info('all', false, true); + print_r($installed_packages); + } + + + private static function get_server_value($maybe_server, $value_key) + { + if (empty($maybe_server)) { + return null; + } + + $raw_value = $maybe_server[$value_key]; + + if (in_array($maybe_server["mode"], ["server_user", "server_tls_user", "server_tls"])) { + return $raw_value == "" ? "server_user_listening" : $raw_value; + } + + if ($maybe_server["mode"] == "p2p_tls") { + // For p2p_tls, ensure we have one client, and return up if it's the case + if ($raw_value == "") { + $has_at_least_one_connection = + is_array($maybe_server["conns"]) && count($maybe_server["conns"]) > 0; + + return $has_at_least_one_connection ? "up" : "down"; + } + } + + return $raw_value; + } + + private static function get_ipsec_status($ike_id, $req_id = -1, $value_key = 'state') + { + + require_once("ipsec.inc"); + $config = PfEnv::cfg(); + PfEnv::init_config_arr(array('ipsec', 'phase1')); + + $a_phase1 = &$config['ipsec']['phase1']; + $conmap = array(); + foreach ($a_phase1 as $ph1ent) { + if (function_exists('get_ipsecifnum')) { + if (PfEnv::get_ipsecifnum($ph1ent['ikeid'], 0)) { + $cname = "con" . PfEnv::get_ipsecifnum($ph1ent['ikeid'], 0); + } else { + $cname = "con{$ph1ent['ikeid']}00000"; + } + } else { + $cname = ipsec_conid($ph1ent); + } + $conmap[$cname] = $ph1ent['ikeid']; + } + + $status = PfEnv::ipsec_list_sa(); + $ipsecconnected = array(); + + $carp_status = self::pfz_carp_status(false); + + //Phase-Status match borrowed from status_ipsec.php + if (is_array($status)) { + foreach ($status as $l_ikeid => $ikesa) { + + if (isset($ikesa['con-id'])) { + $con_id = substr($ikesa['con-id'], 3); + } else { + $con_id = filter_var($ike_id, FILTER_SANITIZE_NUMBER_INT); + } + $con_name = "con" . $con_id; + if ($ikesa['version'] == 1) { + $ph1idx = $conmap[$con_name]; + $ipsecconnected[$ph1idx] = $ph1idx; + } else { + if (!PfEnv::ipsec_ikeid_used($con_id)) { + // probably a v2 with split connection then + $ph1idx = $conmap[$con_name]; + $ipsecconnected[$ph1idx] = $ph1idx; + } else { + $ipsecconnected[$con_id] = $ph1idx = $con_id; + } + } + if ($ph1idx == $ike_id) { + if ($req_id != -1) { + // Asking for Phase2 Status Value + foreach ($ikesa['child-sas'] as $childsas) { + if ($childsas['reqid'] == $req_id) { + if (strtolower($childsas['state']) == 'rekeyed') { + //if state is rekeyed go on + $tmp_value = $childsas[$value_key]; + } else { + $tmp_value = $childsas[$value_key]; + break; + } + } + } + } else { + $tmp_value = $ikesa[$value_key]; + } + + break; + } + } + } + + if ($value_key == "state") { + $v = self::pfz_value_mapping('ipsec.state', strtolower($tmp_value)); + + return ($carp_status != 0) ? $v + (10 * ($carp_status - 1)) : $v; + } + + return $tmp_value; + } + // DHCP Checks (copy of status_dhcp_leases.php, waiting for pfsense 2.5) - public static function pfz_remove_duplicate($array, $field) + private static function remove_duplicates($array, $field): array { foreach ($array as $sub) { $cmp[] = $sub[$field]; @@ -1092,7 +1206,7 @@ class PfzCommands } // Get DHCP Arrays (copied from status_dhcp_leases.php, waiting for pfsense 2.5, in order to use system_get_dhcpleases();) - public static function pfz_dhcp_get($value_key) + private static function get_dhcp($value_key) { @@ -1226,11 +1340,11 @@ class PfzCommands } /* remove duplicate items by mac address */ if (count($leases) > 0) { - $leases = self::pfz_remove_duplicate($leases, "ip"); + $leases = self::remove_duplicates($leases, "ip"); } if (count($pools) > 0) { - $pools = self::pfz_remove_duplicate($pools, "name"); + $pools = self::remove_duplicates($pools, "name"); asort($pools); } @@ -1248,37 +1362,17 @@ class PfzCommands } - // Gateway Discovery - public static function pfz_gw_rawstatus() - { - echo implode(",", - array_map( - fn($gw) => sprintf("%s.%s", $gw['name'], $gw['status']), - PfEnv::return_gateways_status(true))); - } - - public static function pfz_dhcp_check_failover() + private static function check_dhcp_failover() { // Check DHCP Failover Status // Returns number of failover pools which state is not normal or // different than peer state - $failover = self::pfz_dhcp_get("failover"); + $failover = self::get_dhcp("failover"); return count(array_filter($failover, fn($f) => ($f["mystate"] != "normal") || ($f["mystate"] != $f["peerstate"]))); } - public static function pfz_dhcp($section, $value_key = "") - { - $is_known_section = array_key_exists($section, DHCP_SECTIONS); - if (!$is_known_section) { - return; - } - - DHCP_SECTIONS[$section](); - } - - // Packages - public static function pfz_packages_uptodate() + private static function get_outdated_packages() { require_once("pkg-utils.inc"); $installed_packages = PfEnv::get_pkg_info("all", false, true); @@ -1289,80 +1383,6 @@ class PfzCommands fn($p) => $p["version"] != $p["installed_version"])); } - // System Information - public static function pfz_get_system_value($section) - { - if ($section === "packages_update") { - echo self::pfz_packages_uptodate(); - return; - } - - $system_pkg_version = PfEnv::get_system_pkg_version(); - $version = $system_pkg_version["version"]; - $installed_version = $system_pkg_version["installed_version"]; - - if ($section === "new_version_available") { - echo Util::b2int($version != $installed_version); - return; - } - - if (array_key_exists($section, $system_pkg_version)) { - echo $system_pkg_version[$section]; - } - } - - // S.M.A.R.T Status - // Taken from /usr/local/www/widgets/widgets/smart_status.widget.php - public static function pfz_get_smart_status() - { - foreach (PfEnv::get_smart_drive_list() as $dev) { ## for each found drive do - $dev_state = trim(exec("smartctl -H /dev/$dev | awk -F: '/^SMART overall-health self-assessment test result/ {print $2;exit} -/^SMART Health Status/ {print $2;exit}'")); ## get SMART state from drive - $is_known_state = array_key_exists($dev_state, SMART_DEV_STATUS); - if (!$is_known_state) { - return SMART_ERROR; // ED This is probably a bug, status should be echoed - } - - $status = SMART_DEV_STATUS[$dev_state]; - if ($status !== SMART_OK) { - return $status; // ED This is probably a bug, status should be echoed - } - } - - echo SMART_OK; - } - - // Certificats validity date - public static function pfz_get_cert_date($value_key) - { - $config = PfEnv::cfg(); - - $value = 0; - foreach (array("cert", "ca") as $cert_type) { - switch ($value_key) { - case "validFrom.max": - foreach ($config[$cert_type] as $cert) { - $certinfo = openssl_x509_parse(base64_decode($cert[PfEnv::CRT])); - if ($value == 0 or $value < $certinfo['validFrom_time_t']) $value = $certinfo['validFrom_time_t']; - } - break; - case "validTo.min": - foreach ($config[$cert_type] as $cert) { - $certinfo = openssl_x509_parse(base64_decode($cert[PfEnv::CRT])); - if ($value == 0 or $value > $certinfo['validTo_time_t']) $value = $certinfo['validTo_time_t']; - } - break; - } - } - echo $value; - } - - // File is present - public static function pfz_file_exists($filename) - { - echo Util::b2int(file_exists($filename)); - } - // Value mappings // Each value map is represented by an associative array public static function pfz_value_mapping($value_name, $value, $default_value = "0") @@ -1383,15 +1403,7 @@ class PfzCommands return $is_value_with_known_mapping ? $value_mapping[$value] : $default_value; } - public static function pfz_discovery($section) - { - $is_known_section = in_array(strtolower($section), DISCOVERY_SECTION_HANDLERS); - if (!$is_known_section) { - return; - } - DISCOVERY_SECTION_HANDLERS[$section](); - } } function build_method_lookup(string $clazz): array From 866a8171f20fd4b70ce30f4f7c43f7ab40c9c7b4 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Fri, 18 Feb 2022 13:07:37 +0100 Subject: [PATCH 18/93] Hide public method and rename --- pfsense_zbx.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index f5bedf6..a37cad9 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -632,7 +632,7 @@ class PfzCommands if ($gws[$gw]["substatus"] <> "none") $value = $gws[$gw]["substatus"]; - $value = self::pfz_value_mapping("gateway.status", $value); + $value = self::get_value_mapping("gateway.status", $value); } echo $value; } @@ -654,7 +654,7 @@ class PfzCommands public static function pfz_openvpn_servervalue($server_id, $value_key) { - $servers = self::get_all_openvpn_servers(); + $servers = PfzOpenVpn::get_all_openvpn_servers(); $maybe_server = Util::array_first($servers, fn($s) => $s['vpnid'] == $server_id); @@ -666,7 +666,7 @@ class PfzCommands } if (in_array($value_key, ["status", "mode"])) { - echo PfzCommands::pfz_value_mapping("openvpn.server.status", $server_value); + echo self::get_value_mapping("openvpn.server.status", $server_value); return; } @@ -847,7 +847,7 @@ class PfzCommands } // System Information - public static function pfz_get_system_value($section) + public static function pfz_system($section) { if ($section === "packages_update") { echo self::get_outdated_packages(); @@ -898,7 +898,7 @@ class PfzCommands return; } - echo self::pfz_value_mapping("ipsec." . $value_key, $maybe_ike_match[$value_key]); + echo self::get_value_mapping("ipsec." . $value_key, $maybe_ike_match[$value_key]); } public static function pfz_ipsec_ph2($uniqid, $value_key) @@ -927,7 +927,7 @@ class PfzCommands if ($value_key == 'disabled') $value = "1"; else - $value = self::pfz_value_mapping("ipsec_ph2." . $value_key, $data[$value_key], $data[$value_key]); + $value = self::get_value_mapping("ipsec_ph2." . $value_key, $data[$value_key], $data[$value_key]); break; } } @@ -1184,7 +1184,7 @@ class PfzCommands } if ($value_key == "state") { - $v = self::pfz_value_mapping('ipsec.state', strtolower($tmp_value)); + $v = self::get_value_mapping('ipsec.state', strtolower($tmp_value)); return ($carp_status != 0) ? $v + (10 * ($carp_status - 1)) : $v; } @@ -1385,7 +1385,7 @@ class PfzCommands // Value mappings // Each value map is represented by an associative array - public static function pfz_value_mapping($value_name, $value, $default_value = "0") + private static function get_value_mapping($value_name, $value, $default_value = "0") { $is_known_value_name = array_key_exists($value_name, VALUE_MAPPINGS); if (!$is_known_value_name) { From 384e3429b54d226c03f113ec5632114725c5d56c Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Fri, 18 Feb 2022 14:49:08 +0100 Subject: [PATCH 19/93] Simplify command discovery --- pfsense_zbx.php | 643 ++++++++++++++++++++++++------------------------ 1 file changed, 319 insertions(+), 324 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index a37cad9..0abfaf6 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -90,12 +90,11 @@ const SMART_OK = 0; const SMART_UNKNOWN = 2; const SMART_ERROR = 1; -define('SMART_DEV_STATUS', [ +const SMART_DEV_STATUS = [ SMART_DEV_PASSED => SMART_OK, SMART_DEV_OK => SMART_OK, SMART_DEV_UNKNOWN => SMART_UNKNOWN -]); - +]; define("SERVICES_VALUES", [ "status" => function ($service) { @@ -133,22 +132,17 @@ class PfEnv return call_user_func($caller_function_name, ...func_get_args()); } - public static function openvpn_get_active_servers() + public static function convert_friendly_interface_to_friendly_descr() { return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); } - public static function install_cron_job() + public static function get_carp_status() { return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); } - public static function openvpn_get_active_clients() - { - return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); - } - - public static function system_get_dhcpleases() + public static function get_carp_interface_status() { return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); } @@ -158,11 +152,6 @@ class PfEnv return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); } - public static function get_services() - { - return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); - } - public static function get_configured_interface_with_descr() { return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); @@ -178,22 +167,17 @@ class PfEnv return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); } - public static function get_smart_drive_list() - { - return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); - } - - public static function is_service_enabled() - { - return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); - } - public static function get_service_status() { return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); } - public static function init_config_arr() + public static function get_services() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function get_smart_drive_list() { return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); } @@ -203,37 +187,27 @@ class PfEnv return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); } - public static function ipsec_list_sa() - { - return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); - } - - public static function return_gateways_status() - { - return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); - } - public static function get_pkg_info() { return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); } - public static function convert_friendly_interface_to_friendly_descr() - { - return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); - } - - public static function get_carp_interface_status() - { - return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); - } - public static function get_single_sysctl() { return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); } - public static function get_carp_status() + public static function get_system_pkg_version() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function init_config_arr() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function install_cron_job() { return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); } @@ -243,7 +217,32 @@ class PfEnv return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); } - public static function get_system_pkg_version() + public static function ipsec_list_sa() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function is_service_enabled() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function openvpn_get_active_clients() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function openvpn_get_active_servers() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function return_gateways_status() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + + public static function system_get_dhcpleases() { return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); } @@ -286,7 +285,7 @@ class Util class PfzDiscoveries { - public static function pfz_gw() + public static function gw() { $gws = PfEnv::return_gateways_status(true); @@ -301,6 +300,209 @@ class PfzDiscoveries echo $json_string; } + public static function wan($is_wan = false, $is_cron = false) + { + self::discover_interface(true); + } + + public static function temperature_sensors() + { + $json_string = '{"data":['; + $sensors = []; + exec("sysctl -a | grep temperature | cut -d ':' -f 1", $sensors, $code); + if ($code != 0) { + echo ""; + return; + } else { + foreach ($sensors as $sensor) { + $json_string .= '{"{#SENSORID}":"' . $sensor . '"'; + $json_string .= '},'; + } + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + } + + public static function openvpn_server() + { + $servers = PfzOpenVpn::get_all_openvpn_servers(); + + $json_string = '{"data":['; + + foreach ($servers as $server) { + $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $server['name'])); + $json_string .= '{"{#SERVER}":"' . $server['vpnid'] . '"'; + $json_string .= ',"{#NAME}":"' . $name . '"'; + $json_string .= '},'; + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + } + + // OpenVPN Server/User-Auth Discovery + public static function openvpn_server_user() + { + $servers = PfzOpenVpn::get_all_openvpn_servers(); + + $json_string = '{"data":['; + + foreach ($servers as $server) { + if (($server['mode'] == 'server_user') || ($server['mode'] == 'server_tls_user') || ($server['mode'] == 'server_tls')) { + if (is_array($server['conns'])) { + $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $server['name'])); + + foreach ($server['conns'] as $conn) { + + $common_name = Util::replace_special_chars($conn['common_name']); + + $json_string .= '{"{#SERVERID}":"' . $server['vpnid'] . '"'; + $json_string .= ',"{#SERVERNAME}":"' . $name . '"'; + $json_string .= ',"{#UNIQUEID}":"' . $server['vpnid'] . '+' . $common_name . '"'; + $json_string .= ',"{#USERID}":"' . $conn['common_name'] . '"'; + $json_string .= '},'; + } + } + } + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + } + + // OpenVPN Client Discovery + public static function openvpn_client() + { + $clients = PfEnv::openvpn_get_active_clients(); + + $json_string = '{"data":['; + + foreach ($clients as $client) { + $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $client['name'])); + $json_string .= '{"{#CLIENT}":"' . $client['vpnid'] . '"'; + $json_string .= ',"{#NAME}":"' . $name . '"'; + $json_string .= '},'; + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + } + + // Services Discovery + // 2020-03-27: Added space replace with __ for issue #12 + public static function services_discovery() + { + $services = PfEnv::get_services(); + + $json_string = '{"data":['; + + foreach ($services as $service) { + if (!empty($service['name'])) { + + $status = PfEnv::get_service_status($service); + if ($status = "") $status = 0; + + $id = ""; + //id for OpenVPN + if (!empty($service['id'])) $id = "." . $service["id"]; + //zone for Captive Portal + if (!empty($service['zone'])) $id = "." . $service["zone"]; + + $json_string .= '{"{#SERVICE}":"' . str_replace(" ", "__", $service['name']) . $id . '"'; + $json_string .= ',"{#DESCRIPTION}":"' . $service['description'] . '"'; + $json_string .= '},'; + } + } + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + } + + public static function interfaces($is_wan = false, $is_cron = false) + { + self::discover_interface(); + } + + // IPSEC Discovery + public static function ipsec_ph1() + { + + require_once("ipsec.inc"); + $config = PfEnv::cfg(); + PfEnv::init_config_arr(array('ipsec', 'phase1')); + $a_phase1 = &$config['ipsec']['phase1']; + + $json_string = '{"data":['; + + foreach ($a_phase1 as $data) { + $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; + $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; + $json_string .= '},'; + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + + } + + public static function ipsec_ph2() + { + require_once("ipsec.inc"); + + $config = PfEnv::cfg(); + PfEnv::init_config_arr(array('ipsec', 'phase2')); + $a_phase2 = &$config['ipsec']['phase2']; + + $json_string = '{"data":['; + + foreach ($a_phase2 as $data) { + $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; + $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; + $json_string .= ',"{#UNIQID}":"' . $data['uniqid'] . '"'; + $json_string .= ',"{#REQID}":"' . $data['reqid'] . '"'; + $json_string .= ',"{#EXTID}":"' . $data['ikeid'] . '.' . $data['reqid'] . '"'; + $json_string .= '},'; + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + + } + + public static function dhcpfailover() + { + //System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait + require_once("system.inc"); + $leases = PfEnv::system_get_dhcpleases(); + + $json_string = '{"data":['; + + if (count($leases['failover']) > 0) { + foreach ($leases['failover'] as $data) { + $json_string .= '{"{#FAILOVER_GROUP}":"' . str_replace(" ", "__", $data['name']) . '"'; + } + } + + $json_string = rtrim($json_string, ","); + $json_string .= "]}"; + + echo $json_string; + } + // Interface Discovery // Improved performance private static function discover_interface($is_wan = false, $is_cron = false) @@ -351,194 +553,12 @@ class PfzDiscoveries echo $json_string; } - - public static function pfz_wan($is_wan = false, $is_cron = false) - { - self::discover_interface(true); - } - - public static function pfz_openvpn_server() - { - $servers = PfzOpenVpn::get_all_openvpn_servers(); - - $json_string = '{"data":['; - - foreach ($servers as $server) { - $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $server['name'])); - $json_string .= '{"{#SERVER}":"' . $server['vpnid'] . '"'; - $json_string .= ',"{#NAME}":"' . $name . '"'; - $json_string .= '},'; - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; - } - - // OpenVPN Server/User-Auth Discovery - public static function pfz_openvpn_server_user() - { - $servers = PfzOpenVpn::get_all_openvpn_servers(); - - $json_string = '{"data":['; - - foreach ($servers as $server) { - if (($server['mode'] == 'server_user') || ($server['mode'] == 'server_tls_user') || ($server['mode'] == 'server_tls')) { - if (is_array($server['conns'])) { - $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $server['name'])); - - foreach ($server['conns'] as $conn) { - - $common_name = Util::replace_special_chars($conn['common_name']); - - $json_string .= '{"{#SERVERID}":"' . $server['vpnid'] . '"'; - $json_string .= ',"{#SERVERNAME}":"' . $name . '"'; - $json_string .= ',"{#UNIQUEID}":"' . $server['vpnid'] . '+' . $common_name . '"'; - $json_string .= ',"{#USERID}":"' . $conn['common_name'] . '"'; - $json_string .= '},'; - } - } - } - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; - } - - // OpenVPN Client Discovery - public static function pfz_openvpn_client() - { - $clients = PfEnv::openvpn_get_active_clients(); - - $json_string = '{"data":['; - - foreach ($clients as $client) { - $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $client['name'])); - $json_string .= '{"{#CLIENT}":"' . $client['vpnid'] . '"'; - $json_string .= ',"{#NAME}":"' . $name . '"'; - $json_string .= '},'; - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; - } - - // Services Discovery - // 2020-03-27: Added space replace with __ for issue #12 - public static function pfz_services_discovery() - { - $services = PfEnv::get_services(); - - $json_string = '{"data":['; - - foreach ($services as $service) { - if (!empty($service['name'])) { - - $status = PfEnv::get_service_status($service); - if ($status = "") $status = 0; - - $id = ""; - //id for OpenVPN - if (!empty($service['id'])) $id = "." . $service["id"]; - //zone for Captive Portal - if (!empty($service['zone'])) $id = "." . $service["zone"]; - - $json_string .= '{"{#SERVICE}":"' . str_replace(" ", "__", $service['name']) . $id . '"'; - $json_string .= ',"{#DESCRIPTION}":"' . $service['description'] . '"'; - $json_string .= '},'; - } - } - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; - } - - public static function pfz_interfaces($is_wan = false, $is_cron = false) - { - self::discover_interface(); - } - - // IPSEC Discovery - public static function pfz_ipsec_ph1() - { - - require_once("ipsec.inc"); - $config = PfEnv::cfg(); - PfEnv::init_config_arr(array('ipsec', 'phase1')); - $a_phase1 = &$config['ipsec']['phase1']; - - $json_string = '{"data":['; - - foreach ($a_phase1 as $data) { - $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; - $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; - $json_string .= '},'; - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; - - } - - public static function pfz_ipsec_ph2() - { - require_once("ipsec.inc"); - - $config = PfEnv::cfg(); - PfEnv::init_config_arr(array('ipsec', 'phase2')); - $a_phase2 = &$config['ipsec']['phase2']; - - $json_string = '{"data":['; - - foreach ($a_phase2 as $data) { - $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; - $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; - $json_string .= ',"{#UNIQID}":"' . $data['uniqid'] . '"'; - $json_string .= ',"{#REQID}":"' . $data['reqid'] . '"'; - $json_string .= ',"{#EXTID}":"' . $data['ikeid'] . '.' . $data['reqid'] . '"'; - $json_string .= '},'; - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; - - } - - public static function pfz_dhcpfailover() - { - //System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait - require_once("system.inc"); - $leases = PfEnv::system_get_dhcpleases(); - - $json_string = '{"data":['; - - if (count($leases['failover']) > 0) { - foreach ($leases['failover'] as $data) { - $json_string .= '{"{#FAILOVER_GROUP}":"' . str_replace(" ", "__", $data['name']) . '"'; - } - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; - } } class PfzSpeedtest { // Interface Speedtest - public static function pfz_interface_speedtest_value($if_name, $value) + public static function interface_speedtest_value($if_name, $value) { $tvalue = explode(".", $value); @@ -564,14 +584,14 @@ class PfzSpeedtest // Installs a cron job for speedtests - public static function pfz_speedtest_cron_install($enable = true) + public static function speedtest_cron_install($enable = true) { //Install Cron Job $command = "/usr/local/bin/php " . __FILE__ . " speedtest_cron"; PfEnv::install_cron_job($command, $enable, $minute = "*/15", "*", "*", "*", "*", "root", true); } - public static function pfz_speedtest_exec($if_name, $ip_address): bool + public static function speedtest_exec($if_name, $ip_address): bool { $filename = "/tmp/speedtest-$if_name"; @@ -612,17 +632,17 @@ class PfzOpenVpn class PfzCommands { - public static function pfz_discovery($section) + public static function discovery($section) { $is_known_section = in_array(strtolower($section), DISCOVERY_SECTION_HANDLERS); if (!$is_known_section) { return; } - DISCOVERY_SECTION_HANDLERS[$section](); + PfzDiscoveries::{$section}(); } - public static function pfz_gw_value($gw, $value_key) + public static function gw_value($gw, $value_key) { $gws = PfEnv::return_gateways_status(true); if (array_key_exists($gw, $gws)) { @@ -638,7 +658,7 @@ class PfzCommands } } - public static function pfz_gw_status() + public static function gw_status() { echo implode(",", array_map( @@ -646,13 +666,13 @@ class PfzCommands PfEnv::return_gateways_status(true))); } - public static function pfz_if_speedtest_value($if_name, $value) + public static function if_speedtest_value($if_name, $value) { - PfzSpeedtest::pfz_speedtest_cron_install(); - PfzSpeedtest::pfz_interface_speedtest_value($if_name, $value); + PfzSpeedtest::speedtest_cron_install(); + PfzSpeedtest::interface_speedtest_value($if_name, $value); } - public static function pfz_openvpn_servervalue($server_id, $value_key) + public static function openvpn_servervalue($server_id, $value_key) { $servers = PfzOpenVpn::get_all_openvpn_servers(); @@ -673,39 +693,17 @@ class PfzCommands echo $server_value; } - private static function pfz_openvpn_server_uservalue_($unique_id, $value_key, $default = "") + public static function openvpn_server_uservalue($unique_id, $value_key) { - $unique_id = Util::replace_special_chars($unique_id, true); - $atpos = strpos($unique_id, '+'); - $server_id = substr($unique_id, 0, $atpos); - $user_id = substr($unique_id, $atpos + 1); - - $servers = PfzOpenVpn::get_all_openvpn_servers(); - foreach ($servers as $server) { - if ($server['vpnid'] == $server_id) { - foreach ($server['conns'] as $conn) { - if ($conn['common_name'] == $user_id) { - $value = $conn[$value_key]; - } - } - } - } - - return ($value == "") ? $default : $value; + return self::get_openvpn_server_uservalue_($unique_id, $value_key); } - public static function pfz_openvpn_server_uservalue($unique_id, $value_key) + public static function openvpn_server_uservalue_numeric($unique_id, $value_key) { - return self::pfz_openvpn_server_uservalue_($unique_id, $value_key); + return self::get_openvpn_server_uservalue_($unique_id, $value_key, "0"); } - public static function pfz_openvpn_server_uservalue_numeric($unique_id, $value_key) - { - return self::pfz_openvpn_server_uservalue_($unique_id, $value_key, "0"); - } - - - public static function pfz_openvpn_clientvalue($client_id, $value_key, $fallback_value = "none") + public static function openvpn_clientvalue($client_id, $value_key, $fallback_value = "none") { $clients = PfEnv::openvpn_get_active_clients(); @@ -725,7 +723,7 @@ class PfzCommands return ($maybe_value == "") ? $fallback_value : $maybe_value; } - public static function pfz_service_value($name, $value) + public static function service_value($name, $value) { $services = PfEnv::get_services(); $name = str_replace("__", " ", $name); @@ -759,44 +757,19 @@ class PfzCommands } } - function pfz_temperature_sensors_discovery() + + public static function temperature($sensorid) { - - - $json_string = '{"data":['; - $sensors = []; - exec("sysctl -a | grep temperature | cut -d ':' -f 1", $sensors, $code); - if ($code != 0) { - echo ""; - return; - } else { - foreach ($sensors as $sensor) { - $json_string .= '{"{#SENSORID}":"' . $sensor . '"'; - $json_string .= '},'; - } - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; - - } - - function pfz_get_temperature($sensorid) - { - exec("sysctl '$sensorid' | cut -d ':' -f 2", $value, $code); if ($code != 0 or count($value) != 1) { echo ""; return; - } else { - echo trim($value[0]); } - + + echo trim($value[0]); } - public static function pfz_carp_status($echo = true) + public static function carp_status($echo = true): int { //Detect CARP Status $config = PfEnv::cfg(); @@ -847,7 +820,7 @@ class PfzCommands } // System Information - public static function pfz_system($section) + public static function system($section) { if ($section === "packages_update") { echo self::get_outdated_packages(); @@ -868,7 +841,7 @@ class PfzCommands } } - public static function pfz_ipsec_ph1($ike_id, $value_key) + public static function ipsec_ph1($ike_id, $value_key) { // Get Value from IPsec Phase 1 Configuration // If Getting "disabled" value only check item presence in config array @@ -901,7 +874,7 @@ class PfzCommands echo self::get_value_mapping("ipsec." . $value_key, $maybe_ike_match[$value_key]); } - public static function pfz_ipsec_ph2($uniqid, $value_key) + public static function ipsec_ph2($uniqid, $value_key) { require_once("ipsec.inc"); $config = PfEnv::cfg(); @@ -935,7 +908,7 @@ class PfzCommands echo $value; } - public static function pfz_dhcp($section) + public static function dhcp($section) { if ($section != "failover") { return; @@ -945,12 +918,12 @@ class PfzCommands } // File is present - public static function pfz_file_exists($filename) + public static function file_exists($filename) { echo Util::b2int(file_exists($filename)); } - public static function pfz_speedtest_cron() + public static function speedtest_cron() { require_once("services.inc"); $ifdescrs = PfEnv::get_configured_interface_with_descr(true); @@ -958,7 +931,7 @@ class PfzCommands $pf_interface_name = ''; $subvalue = false; - $ifcs = PfzDiscoveries::pfz_interface_discovery(true, true); + $ifcs = PfzDiscoveries::interface_discovery(true, true); foreach ($ifcs as $if_name) { @@ -970,18 +943,18 @@ class PfzCommands } } - PfzSpeedtest::pfz_speedtest_exec($if_name, $if_info['ipaddr']); + PfzSpeedtest::speedtest_exec($if_name, $if_info['ipaddr']); } } - public static function pfz_cron_cleanup() + public static function cron_cleanup() { - PfzSpeedtest::pfz_speedtest_cron_install(false); + PfzSpeedtest::speedtest_cron_install(false); } // S.M.A.R.T Status // Taken from /usr/local/www/widgets/widgets/smart_status.widget.php - public static function pfz_smart_status() + public static function smart_status() { foreach (PfEnv::get_smart_drive_list() as $dev) { ## for each found drive do $dev_state = trim(exec("smartctl -H /dev/$dev | awk -F: '/^SMART overall-health self-assessment test result/ {print $2;exit} @@ -1000,7 +973,7 @@ class PfzCommands echo SMART_OK; } - public static function pfz_cert_date($value_key) + public static function cert_date($value_key) { $config = PfEnv::cfg(); @@ -1025,7 +998,7 @@ class PfzCommands } // Testing function, for template creating purpose - public static function pfz_test() + public static function test() { $line = "-------------------\n"; @@ -1085,6 +1058,26 @@ class PfzCommands print_r($installed_packages); } + private static function get_openvpn_server_uservalue_($unique_id, $value_key, $default = "") + { + $unique_id = Util::replace_special_chars($unique_id, true); + $atpos = strpos($unique_id, '+'); + $server_id = substr($unique_id, 0, $atpos); + $user_id = substr($unique_id, $atpos + 1); + + $servers = PfzOpenVpn::get_all_openvpn_servers(); + foreach ($servers as $server) { + if ($server['vpnid'] == $server_id) { + foreach ($server['conns'] as $conn) { + if ($conn['common_name'] == $user_id) { + $value = $conn[$value_key]; + } + } + } + } + + return ($value == "") ? $default : $value; + } private static function get_server_value($maybe_server, $value_key) { @@ -1136,7 +1129,7 @@ class PfzCommands $status = PfEnv::ipsec_list_sa(); $ipsecconnected = array(); - $carp_status = self::pfz_carp_status(false); + $carp_status = self::carp_status(false); //Phase-Status match borrowed from status_ipsec.php if (is_array($status)) { @@ -1402,19 +1395,21 @@ class PfzCommands return $is_value_with_known_mapping ? $value_mapping[$value] : $default_value; } - - } function build_method_lookup(string $clazz): array { - $all_public_methods = get_class_methods($clazz); + try { + $reflector = new ReflectionClass($clazz); - $just_commands = array_filter($all_public_methods, fn($name) => substr($name, 0, 3) === "pfz"); + $all_methods = $reflector->getMethods(); - return array_map( - fn($command_name) => str_ireplace("pfz_", "", $command_name), - $just_commands); + $commands = array_filter($all_methods, fn($method) => $method->isStatic() && $method->isPublic()); + + return array_map(fn(ReflectionMethod $method) => $method->getName(), $commands); + } catch (Exception $e) { + return []; + } } function main($arguments) @@ -1431,11 +1426,11 @@ function main($arguments) $is_known_command = in_array($command, COMMAND_HANDLERS); if (!$is_known_command) { - PfzCommands::pfz_test(); + PfzCommands::test(); exit; } - PfzCommands::{"pfz_$command"}(...$parameters); + PfzCommands::{$command}(...$parameters); } main($argv); From 5d22a253f395e42ba359de3dc18cf4d322f04805 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Fri, 18 Feb 2022 16:11:08 +0100 Subject: [PATCH 20/93] Fix a few methods --- pfsense_zbx.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 0abfaf6..86b549b 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -300,7 +300,7 @@ class PfzDiscoveries echo $json_string; } - public static function wan($is_wan = false, $is_cron = false) + public static function wan() { self::discover_interface(true); } @@ -399,7 +399,7 @@ class PfzDiscoveries // Services Discovery // 2020-03-27: Added space replace with __ for issue #12 - public static function services_discovery() + public static function services() { $services = PfEnv::get_services(); From d882d3a47f52fd1691ad9da8a50380f1b9fec958 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Fri, 18 Feb 2022 16:11:27 +0100 Subject: [PATCH 21/93] Fix a few methods --- pfsense_zbx.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 86b549b..bbd7362 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -428,7 +428,7 @@ class PfzDiscoveries echo $json_string; } - public static function interfaces($is_wan = false, $is_cron = false) + public static function interfaces() { self::discover_interface(); } From a20a9d86f2484d882d038679489a203fbc35a714 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Fri, 18 Feb 2022 17:03:47 +0100 Subject: [PATCH 22/93] Simplify json generation for discoveries --- pfsense_zbx.php | 109 ++++++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 49 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index bbd7362..c9492f7 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -285,19 +285,18 @@ class Util class PfzDiscoveries { + private static function print_json(array $json) + { + echo json_encode([ + "data" => $json + ]); + } + public static function gw() { $gws = PfEnv::return_gateways_status(true); - $json_string = '{"data":['; - foreach ($gws as $gw) { - $json_string .= '{"{#GATEWAY}":"' . $gw['name'] . '"'; - $json_string .= '},'; - } - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; + self::print_json(array_map(fn($gw) => ["{#GATEWAY}" => $gw["name"]], $gws)); } public static function wan() @@ -326,23 +325,44 @@ class PfzDiscoveries echo $json_string; } + private static function sanitize_server_name(string $raw_name): string + { + return trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $raw_name)); + } + public static function openvpn_server() { $servers = PfzOpenVpn::get_all_openvpn_servers(); - $json_string = '{"data":['; + self::print_json(array_map(fn($server) => [ + "{#SERVER}" => $server['vpnid'], + "{#NAME}" => self::sanitize_server_name($server["name"])], + $servers)); + } - foreach ($servers as $server) { - $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $server['name'])); - $json_string .= '{"{#SERVER}":"' . $server['vpnid'] . '"'; - $json_string .= ',"{#NAME}":"' . $name . '"'; - $json_string .= '},'; - } + private static function map_conn(string $server_name, string $vpn_id, array $conn): array + { + return [ + "{#SERVERID}" => $vpn_id, + "{#SERVERNAME}" => $server_name, + "{#UNIQUEID}" => sprintf("%s+%s", $vpn_id, Util::replace_special_chars($conn['common_name'])), + "{#USERID}" => Util::replace_special_chars($conn['common_name']), + ]; + } - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; + private static function map_conns(string $server_name, string $vpn_id, array $conns): array + { + return array_map( + fn($conn) => self::map_conn($server_name, $vpn_id, $conn), + $conns); + } - echo $json_string; + private static function map_server(array $server): array + { + return self::map_conns( + self::sanitize_server_name($server["name"]), + $server["vpnid"], + $server["conns"]); } // OpenVPN Server/User-Auth Discovery @@ -350,31 +370,16 @@ class PfzDiscoveries { $servers = PfzOpenVpn::get_all_openvpn_servers(); - $json_string = '{"data":['; + $servers_with_relevant_mode = + array_filter( + $servers, + fn($server) => in_array($server["mode"], ["server_user", "server_tls_user", "server_tls"])); - foreach ($servers as $server) { - if (($server['mode'] == 'server_user') || ($server['mode'] == 'server_tls_user') || ($server['mode'] == 'server_tls')) { - if (is_array($server['conns'])) { - $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $server['name'])); + $servers_with_conns = array_filter( + $servers_with_relevant_mode, + fn($server) => is_array($server["conns"])); - foreach ($server['conns'] as $conn) { - - $common_name = Util::replace_special_chars($conn['common_name']); - - $json_string .= '{"{#SERVERID}":"' . $server['vpnid'] . '"'; - $json_string .= ',"{#SERVERNAME}":"' . $name . '"'; - $json_string .= ',"{#UNIQUEID}":"' . $server['vpnid'] . '+' . $common_name . '"'; - $json_string .= ',"{#USERID}":"' . $conn['common_name'] . '"'; - $json_string .= '},'; - } - } - } - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; + self::print_json(array_merge(...array_map(fn($s) => self::map_server($s), $servers_with_conns))); } // OpenVPN Client Discovery @@ -399,7 +404,8 @@ class PfzDiscoveries // Services Discovery // 2020-03-27: Added space replace with __ for issue #12 - public static function services() + public + static function services() { $services = PfEnv::get_services(); @@ -428,13 +434,15 @@ class PfzDiscoveries echo $json_string; } - public static function interfaces() + public + static function interfaces() { self::discover_interface(); } // IPSEC Discovery - public static function ipsec_ph1() + public + static function ipsec_ph1() { require_once("ipsec.inc"); @@ -457,7 +465,8 @@ class PfzDiscoveries } - public static function ipsec_ph2() + public + static function ipsec_ph2() { require_once("ipsec.inc"); @@ -483,7 +492,8 @@ class PfzDiscoveries } - public static function dhcpfailover() + public + static function dhcpfailover() { //System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait require_once("system.inc"); @@ -505,7 +515,8 @@ class PfzDiscoveries // Interface Discovery // Improved performance - private static function discover_interface($is_wan = false, $is_cron = false) + private + static function discover_interface($is_wan = false, $is_cron = false) { $ifdescrs = PfEnv::get_configured_interface_with_descr(true); $ifaces = PfEnv::get_interface_arr(); @@ -765,7 +776,7 @@ class PfzCommands echo ""; return; } - + echo trim($value[0]); } From 4d23d9320f758a01a24252cbb7a7ebac6f52519c Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Fri, 18 Feb 2022 17:07:19 +0100 Subject: [PATCH 23/93] Re-order discovery methods --- pfsense_zbx.php | 104 +++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 55 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index c9492f7..4ce6e5d 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -285,13 +285,6 @@ class Util class PfzDiscoveries { - private static function print_json(array $json) - { - echo json_encode([ - "data" => $json - ]); - } - public static function gw() { $gws = PfEnv::return_gateways_status(true); @@ -304,6 +297,11 @@ class PfzDiscoveries self::discover_interface(true); } + private static function sanitize_server_name(string $raw_name): string + { + return trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $raw_name)); + } + public static function temperature_sensors() { $json_string = '{"data":['; @@ -325,11 +323,6 @@ class PfzDiscoveries echo $json_string; } - private static function sanitize_server_name(string $raw_name): string - { - return trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $raw_name)); - } - public static function openvpn_server() { $servers = PfzOpenVpn::get_all_openvpn_servers(); @@ -340,32 +333,6 @@ class PfzDiscoveries $servers)); } - private static function map_conn(string $server_name, string $vpn_id, array $conn): array - { - return [ - "{#SERVERID}" => $vpn_id, - "{#SERVERNAME}" => $server_name, - "{#UNIQUEID}" => sprintf("%s+%s", $vpn_id, Util::replace_special_chars($conn['common_name'])), - "{#USERID}" => Util::replace_special_chars($conn['common_name']), - ]; - } - - private static function map_conns(string $server_name, string $vpn_id, array $conns): array - { - return array_map( - fn($conn) => self::map_conn($server_name, $vpn_id, $conn), - $conns); - } - - private static function map_server(array $server): array - { - return self::map_conns( - self::sanitize_server_name($server["name"]), - $server["vpnid"], - $server["conns"]); - } - - // OpenVPN Server/User-Auth Discovery public static function openvpn_server_user() { $servers = PfzOpenVpn::get_all_openvpn_servers(); @@ -382,7 +349,6 @@ class PfzDiscoveries self::print_json(array_merge(...array_map(fn($s) => self::map_server($s), $servers_with_conns))); } - // OpenVPN Client Discovery public static function openvpn_client() { $clients = PfEnv::openvpn_get_active_clients(); @@ -402,10 +368,7 @@ class PfzDiscoveries echo $json_string; } - // Services Discovery - // 2020-03-27: Added space replace with __ for issue #12 - public - static function services() + public static function services() { $services = PfEnv::get_services(); @@ -434,15 +397,13 @@ class PfzDiscoveries echo $json_string; } - public - static function interfaces() + public static function interfaces() { self::discover_interface(); } // IPSEC Discovery - public - static function ipsec_ph1() + public static function ipsec_ph1() { require_once("ipsec.inc"); @@ -465,8 +426,7 @@ class PfzDiscoveries } - public - static function ipsec_ph2() + public static function ipsec_ph2() { require_once("ipsec.inc"); @@ -492,8 +452,7 @@ class PfzDiscoveries } - public - static function dhcpfailover() + public static function dhcpfailover() { //System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait require_once("system.inc"); @@ -513,10 +472,45 @@ class PfzDiscoveries echo $json_string; } - // Interface Discovery - // Improved performance - private - static function discover_interface($is_wan = false, $is_cron = false) + private static function print_json(array $json) + { + echo json_encode([ + "data" => $json + ]); + } + + private static function sanitize_server_name(string $raw_name): string + { + return trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $raw_name)); + } + + + private static function map_conn(string $server_name, string $vpn_id, array $conn): array + { + return [ + "{#SERVERID}" => $vpn_id, + "{#SERVERNAME}" => $server_name, + "{#UNIQUEID}" => sprintf("%s+%s", $vpn_id, Util::replace_special_chars($conn['common_name'])), + "{#USERID}" => Util::replace_special_chars($conn['common_name']), + ]; + } + + private static function map_conns(string $server_name, string $vpn_id, array $conns): array + { + return array_map( + fn($conn) => self::map_conn($server_name, $vpn_id, $conn), + $conns); + } + + private static function map_server(array $server): array + { + return self::map_conns( + self::sanitize_server_name($server["name"]), + $server["vpnid"], + $server["conns"]); + } + + private static function discover_interface($is_wan = false, $is_cron = false) { $ifdescrs = PfEnv::get_configured_interface_with_descr(true); $ifaces = PfEnv::get_interface_arr(); From 114d51100518c43635d185c237b5206020f4b49b Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Fri, 18 Feb 2022 17:51:59 +0100 Subject: [PATCH 24/93] Simplify a few json discovery serializations --- pfsense_zbx.php | 95 ++++++++++++++----------------------------------- 1 file changed, 27 insertions(+), 68 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 4ce6e5d..b18e9e1 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -329,7 +329,7 @@ class PfzDiscoveries self::print_json(array_map(fn($server) => [ "{#SERVER}" => $server['vpnid'], - "{#NAME}" => self::sanitize_server_name($server["name"])], + "{#NAME}" => self::sanitize_name($server["name"])], $servers)); } @@ -353,48 +353,27 @@ class PfzDiscoveries { $clients = PfEnv::openvpn_get_active_clients(); - $json_string = '{"data":['; - - foreach ($clients as $client) { - $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $client['name'])); - $json_string .= '{"{#CLIENT}":"' . $client['vpnid'] . '"'; - $json_string .= ',"{#NAME}":"' . $name . '"'; - $json_string .= '},'; - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; + self::print_json(array_map(fn($client) => [ + "{#CLIENT}" => $client['vpnid'], + "{#NAME}", self::sanitize_name($client["name"]), + ], $clients)); } public static function services() { $services = PfEnv::get_services(); - $json_string = '{"data":['; + $named_services = array_filter($services, fn($service) => !empty($service['name'])); - foreach ($services as $service) { - if (!empty($service['name'])) { + self::print_json(array_map(function ($service) { + $maybe_id = Util::array_first(array_keys($service), fn($key) => in_array($key, ["id", "zone"])); + $id = is_null($maybe_id) ? "" : $service[$maybe_id]; - $status = PfEnv::get_service_status($service); - if ($status = "") $status = 0; - - $id = ""; - //id for OpenVPN - if (!empty($service['id'])) $id = "." . $service["id"]; - //zone for Captive Portal - if (!empty($service['zone'])) $id = "." . $service["zone"]; - - $json_string .= '{"{#SERVICE}":"' . str_replace(" ", "__", $service['name']) . $id . '"'; - $json_string .= ',"{#DESCRIPTION}":"' . $service['description'] . '"'; - $json_string .= '},'; - } - } - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; + return [ + "{#SERVICE}" => str_replace(" ", "__", $service['name']) . $id, + "{#DESCRIPTION}" => $service['description'], + ]; + }, $named_services)); } public static function interfaces() @@ -402,28 +381,17 @@ class PfzDiscoveries self::discover_interface(); } - // IPSEC Discovery public static function ipsec_ph1() { - require_once("ipsec.inc"); $config = PfEnv::cfg(); PfEnv::init_config_arr(array('ipsec', 'phase1')); $a_phase1 = &$config['ipsec']['phase1']; - $json_string = '{"data":['; - - foreach ($a_phase1 as $data) { - $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; - $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; - $json_string .= '},'; - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; - + self::print_json(array_map(fn($data) => [ + "{#IKEID}" => $data['ikeid'], + "{#NAME}" => $data['descr'], + ], $a_phase1)); } public static function ipsec_ph2() @@ -434,22 +402,13 @@ class PfzDiscoveries PfEnv::init_config_arr(array('ipsec', 'phase2')); $a_phase2 = &$config['ipsec']['phase2']; - $json_string = '{"data":['; - - foreach ($a_phase2 as $data) { - $json_string .= '{"{#IKEID}":"' . $data['ikeid'] . '"'; - $json_string .= ',"{#NAME}":"' . $data['descr'] . '"'; - $json_string .= ',"{#UNIQID}":"' . $data['uniqid'] . '"'; - $json_string .= ',"{#REQID}":"' . $data['reqid'] . '"'; - $json_string .= ',"{#EXTID}":"' . $data['ikeid'] . '.' . $data['reqid'] . '"'; - $json_string .= '},'; - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; - + self::print_json(array_map(fn($data) => [ + "{#IKEID}" => $data['ikeid'], + "{#NAME}" => $data['descr'], + "{#UNIQID}" => $data['uniqid'], + "{#REQID}" => $data['reqid'], + "{#EXTID}" => $data['ikeid'] . '.' . $data['reqid'], + ], $a_phase2)); } public static function dhcpfailover() @@ -479,7 +438,7 @@ class PfzDiscoveries ]); } - private static function sanitize_server_name(string $raw_name): string + private static function sanitize_name(string $raw_name): string { return trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $raw_name)); } @@ -505,7 +464,7 @@ class PfzDiscoveries private static function map_server(array $server): array { return self::map_conns( - self::sanitize_server_name($server["name"]), + self::sanitize_name($server["name"]), $server["vpnid"], $server["conns"]); } From 8d2f5302b2ba70d8f1557e3429dff9c3b624b417 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Fri, 18 Feb 2022 21:29:15 +0100 Subject: [PATCH 25/93] Simplify interface_discovery serialization --- pfsense_zbx.php | 103 ++++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index b18e9e1..7470ce7 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -417,18 +417,10 @@ class PfzDiscoveries require_once("system.inc"); $leases = PfEnv::system_get_dhcpleases(); - $json_string = '{"data":['; + self::print_json(array_map(fn($data) => [ + "{#FAILOVER_GROUP}" => str_replace(" ", "__", $data['name']), + ], $leases["failover"])); - if (count($leases['failover']) > 0) { - foreach ($leases['failover'] as $data) { - $json_string .= '{"{#FAILOVER_GROUP}":"' . str_replace(" ", "__", $data['name']) . '"'; - } - } - - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - - echo $json_string; } private static function print_json(array $json) @@ -469,53 +461,61 @@ class PfzDiscoveries $server["conns"]); } - private static function discover_interface($is_wan = false, $is_cron = false) + private static function discover_interface($is_wan = false, $is_cron = false): array { $ifdescrs = PfEnv::get_configured_interface_with_descr(true); $ifaces = PfEnv::get_interface_arr(); - $ifcs = array(); - $if_ret = array(); - $json_string = '{"data":['; - - foreach ($ifdescrs as $ifname => $ifdescr) { - $ifinfo = PfEnv::get_interface_info($ifname); - $ifinfo["description"] = $ifdescr; - $ifcs[$ifname] = $ifinfo; - } - - foreach ($ifaces as $hwif) { - - $ifdescr = $hwif; - $has_gw = false; - $is_vpn = false; - $has_public_ip = false; - - foreach ($ifcs as $ifc => $ifinfo) { - if ($ifinfo["hwif"] == $hwif) { - $ifdescr = $ifinfo["description"]; - if (array_key_exists("gateway", $ifinfo)) $has_gw = true; - // Issue #81 - https://stackoverflow.com/a/13818647/15093007 - if (filter_var($ifinfo["ipaddr"], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) $has_public_ip = true; - if (strpos($ifinfo["if"], "ovpn") !== false) $is_vpn = true; - break; - } + if (!$is_wan) { + if (!$is_cron) { + self::print_json([]); } - if (($is_wan == false) || (($is_wan == true) && (($has_gw == true) || ($has_public_ip == true)) && ($is_vpn == false))) { - $if_ret[] = $hwif; - $json_string .= '{"{#IFNAME}":"' . $hwif . '"'; - $json_string .= ',"{#IFDESCR}":"' . $ifdescr . '"'; - $json_string .= '},'; + return []; + } + + $ifcs = array_reduce(array_keys($ifdescrs), function ($p, $c) use ($ifdescrs) { + return [ + ...$p, + $c => [ + ...PfEnv::get_interface_info($c), + "description" => $ifdescrs[$c], + ]]; + }, []); + + $ifaces_filtered = array_filter($ifaces, function ($hwif) use ($ifcs) { + $maybe_ifinfo = Util::array_first($ifcs, fn($v) => ($v["hwif"] == $hwif)); + if (!$maybe_ifinfo) { + return false; } + $has_gw = array_key_exists("gateway", $maybe_ifinfo); + // Issue #81 - https://stackoverflow.com/a/13818647/15093007 + $has_public_ip = + filter_var( + $maybe_ifinfo["ipaddr"], + FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE); + $is_vpn = strpos($maybe_ifinfo["if"], "ovpn") !== false; + + return ($has_gw || $has_public_ip) && !$is_vpn; + }); + + $json_dict = array_map(function ($is_wan, $hwif) use ($ifcs) { + $maybe_ifinfo = Util::array_first($ifcs, fn($v) => ($v["hwif"] == $hwif)); + + $ifdescr = $maybe_ifinfo["description"]; + + return [ + "{#IFNAME}" => $hwif, + "{#IFDESCR}" => $ifdescr, + ]; + }, $ifaces_filtered); + + if (!$is_cron) { + self::print_json($json_dict); } - $json_string = rtrim($json_string, ","); - $json_string .= "]}"; - if ($is_cron) return $if_ret; - - echo $json_string; + return $ifaces_filtered; } } @@ -546,7 +546,6 @@ class PfzSpeedtest } } - // Installs a cron job for speedtests public static function speedtest_cron_install($enable = true) { @@ -977,7 +976,7 @@ class PfzCommands echo $line; $ifdescrs = PfEnv::get_configured_interface_with_descr(true); - $ifaces = array(); + $ifaces = []; foreach ($ifdescrs as $ifdescr => $ifname) { $ifinfo = PfEnv::get_interface_info($ifdescr); $ifaces[$ifname] = $ifinfo; @@ -1076,7 +1075,7 @@ class PfzCommands PfEnv::init_config_arr(array('ipsec', 'phase1')); $a_phase1 = &$config['ipsec']['phase1']; - $conmap = array(); + $conmap = []; foreach ($a_phase1 as $ph1ent) { if (function_exists('get_ipsecifnum')) { if (PfEnv::get_ipsecifnum($ph1ent['ikeid'], 0)) { @@ -1091,7 +1090,7 @@ class PfzCommands } $status = PfEnv::ipsec_list_sa(); - $ipsecconnected = array(); + $ipsecconnected = []; $carp_status = self::carp_status(false); From aa60579f6e9d5e12cecbbd47231b762f381925c5 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Fri, 18 Feb 2022 22:11:48 +0100 Subject: [PATCH 26/93] Simplify and clean a few methods --- pfsense_zbx.php | 113 ++++++++++++++++++++---------------------------- 1 file changed, 46 insertions(+), 67 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 7470ce7..8975b43 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -461,61 +461,43 @@ class PfzDiscoveries $server["conns"]); } - private static function discover_interface($is_wan = false, $is_cron = false): array + private static function discover_interface($is_wan = false) { - $ifdescrs = PfEnv::get_configured_interface_with_descr(true); - $ifaces = PfEnv::get_interface_arr(); if (!$is_wan) { - if (!$is_cron) { - self::print_json([]); - } - - return []; + self::print_json([]); + return; } - $ifcs = array_reduce(array_keys($ifdescrs), function ($p, $c) use ($ifdescrs) { + $if_descriptions = PfEnv::get_configured_interface_with_descr(true); + + $interfaces = array_map(function ($interface) { + list ($if_name, $description) = $interface; + return [ - ...$p, - $c => [ - ...PfEnv::get_interface_info($c), - "description" => $ifdescrs[$c], - ]]; - }, []); + ...PfEnv::get_interface_info($if_name), + "description" => $description, + ]; + }, array_map(null, array_keys($if_descriptions), array_values($if_descriptions))); - $ifaces_filtered = array_filter($ifaces, function ($hwif) use ($ifcs) { - $maybe_ifinfo = Util::array_first($ifcs, fn($v) => ($v["hwif"] == $hwif)); - if (!$maybe_ifinfo) { - return false; - } - - $has_gw = array_key_exists("gateway", $maybe_ifinfo); + $wan_interfaces = array_filter($interfaces, function ($iface_info_ext) { + $has_gw = array_key_exists("gateway", $iface_info_ext); // Issue #81 - https://stackoverflow.com/a/13818647/15093007 $has_public_ip = filter_var( - $maybe_ifinfo["ipaddr"], + $iface_info_ext["ipaddr"], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE); - $is_vpn = strpos($maybe_ifinfo["if"], "ovpn") !== false; + $is_vpn = strpos($iface_info_ext["if"], "ovpn") !== false; return ($has_gw || $has_public_ip) && !$is_vpn; }); - $json_dict = array_map(function ($is_wan, $hwif) use ($ifcs) { - $maybe_ifinfo = Util::array_first($ifcs, fn($v) => ($v["hwif"] == $hwif)); - - $ifdescr = $maybe_ifinfo["description"]; - + self::print_json(array_map(function ($hwif) { return [ - "{#IFNAME}" => $hwif, - "{#IFDESCR}" => $ifdescr, + "{#IFNAME}" => $hwif['hwif'], + "{#IFDESCR}" => $hwif["description"], ]; - }, $ifaces_filtered); - - if (!$is_cron) { - self::print_json($json_dict); - } - - return $ifaces_filtered; + }, $wan_interfaces)); } } @@ -524,26 +506,19 @@ class PfzSpeedtest // Interface Speedtest public static function interface_speedtest_value($if_name, $value) { - $tvalue = explode(".", $value); + list($tv0, $tv1) = explode(".", $value); - if (count($tvalue) > 1) { - $value = $tvalue[0]; - $subvalue = $tvalue[1]; - } - - //If the interface has a gateway is considered WAN, so let's do the speedtest $filename = "/tmp/speedtest-$if_name"; - - if (file_exists($filename)) { - $speedtest_data = json_decode(file_get_contents($filename), true); - - if (array_key_exists($value, $speedtest_data)) { - if ($subvalue == false) - echo $speedtest_data[$value]; - else - echo $speedtest_data[$value][$subvalue]; - } + if (!file_exists($filename)) { + return; } + + $speedtest_data = json_decode(file_get_contents($filename), true); + if (array_key_exists($value, $speedtest_data)) { + return; + } + + echo empty($tv1) ? $speedtest_data[$value] : $speedtest_data[$tv0][$tv1]; } // Installs a cron job for speedtests @@ -554,7 +529,7 @@ class PfzSpeedtest PfEnv::install_cron_job($command, $enable, $minute = "*/15", "*", "*", "*", "*", "root", true); } - public static function speedtest_exec($if_name, $ip_address): bool + public static function speedtest_exec($if_name, $ip_address) { $filename = "/tmp/speedtest-$if_name"; @@ -566,19 +541,23 @@ class PfzSpeedtest sleep(rand(1, 90)); if ((time() - filemtime($filename) > SPEEDTEST_INTERVAL * 3600) || (file_exists($filename) == false)) { - // file is older than SPEEDTEST_INTERVAL - if ((time() - filemtime($filerun) > 180)) @unlink($filerun); - - if (file_exists($filerun) == false) { - touch($filerun); - $st_command = "/usr/local/bin/speedtest --source $ip_address --json > $filetemp"; - exec($st_command); - rename($filetemp, $filename); - @unlink($filerun); - } + return; } - return true; + // file is older than SPEEDTEST_INTERVAL + if ((time() - filemtime($filerun) > 180)) { + @unlink($filerun); + } + + if (file_exists($filerun)) { + return; + } + + touch($filerun); + $st_command = "/usr/local/bin/speedtest --source $ip_address --json > $filetemp"; + exec($st_command); + rename($filetemp, $filename); + @unlink($filerun); } } From 41ee512e3eb173abc20544e46e4d0619eaef6c59 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sat, 19 Feb 2022 00:08:29 +0100 Subject: [PATCH 27/93] Simplify some low hanging fruit --- pfsense_zbx.php | 293 ++++++++++++++++++++++++------------------------ 1 file changed, 147 insertions(+), 146 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 8975b43..4753528 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -82,6 +82,11 @@ define("VALUE_MAPPINGS", [ "installed" => 1, "rekeyed" => 2]]); +define("CERT_VK_TO_FIELD", [ + "validFrom.max" => "validFrom_time_t", + "validTo.min" => "validTo_time_t", +]); + const SMART_DEV_PASSED = "PASSED"; const SMART_DEV_OK = "OK"; const SMART_DEV_UNKNOWN = ""; @@ -96,6 +101,21 @@ const SMART_DEV_STATUS = [ SMART_DEV_UNKNOWN => SMART_UNKNOWN ]; +const CARP_INCONSISTENT = "INCONSISTENT"; +const CARP_MASTER = "MASTER"; + +const CARP_STATUS_DISABLED = 0; +const CARP_STATUS_OK = 1; +const CARP_STATUS_UNKNOWN = 2; +const CARP_STATUS_INCONSISTENT = 3; +const CARP_STATUS_PROBLEM = 4; + +const CARP_RES = [ + CARP_INCONSISTENT => CARP_STATUS_INCONSISTENT, + CARP_MASTER => CARP_STATUS_OK +]; + + define("SERVICES_VALUES", [ "status" => function ($service) { $status = PfEnv::get_service_status($service); @@ -266,20 +286,30 @@ class Util return (int)$b; } - public static function replace_special_chars($inputstr, $reverse = false) + public static function replace_special_chars(string $input, bool $decode = false): string { - $specialchars = ",',\",`,*,?,[,],{,},~,$,!,&,;,(,),<,>,|,#,@,0x0a"; - $specialchars = explode(",", $specialchars); - $resultstr = $inputstr; + $special_chars = explode(",", ",',\",`,*,?,[,],{,},~,$,!,&,;,(,),<,>,|,#,@,0x0a"); - for ($n = 0; $n < count($specialchars); $n++) { - if ($reverse == false) - $resultstr = str_replace($specialchars[$n], '%%' . $n . '%', $resultstr); - else - $resultstr = str_replace('%%' . $n . '%', $specialchars[$n], $resultstr); + $result = $input; + + foreach ($special_chars as $idx => $plain) { + $encoded = "%%$idx%"; + + list($search, $replace) = $decode ? [$encoded, $plain] : [$plain, $encoded]; + + $result = str_replace($search, $replace, $result); } - return $resultstr; + return $result; + } + + public static function result($result, bool $echo_result = false) + { + if ($echo_result) { + echo $echo_result; + } + + return $result; } } @@ -351,19 +381,15 @@ class PfzDiscoveries public static function openvpn_client() { - $clients = PfEnv::openvpn_get_active_clients(); - self::print_json(array_map(fn($client) => [ "{#CLIENT}" => $client['vpnid'], "{#NAME}", self::sanitize_name($client["name"]), - ], $clients)); + ], PfEnv::openvpn_get_active_clients())); } public static function services() { - $services = PfEnv::get_services(); - - $named_services = array_filter($services, fn($service) => !empty($service['name'])); + $named_services = array_filter(PfEnv::get_services(), fn($service) => !empty($service['name'])); self::print_json(array_map(function ($service) { $maybe_id = Util::array_first(array_keys($service), fn($key) => in_array($key, ["id", "zone"])); @@ -413,7 +439,7 @@ class PfzDiscoveries public static function dhcpfailover() { - //System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait + // System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait require_once("system.inc"); $leases = PfEnv::system_get_dhcpleases(); @@ -432,10 +458,9 @@ class PfzDiscoveries private static function sanitize_name(string $raw_name): string { - return trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $raw_name)); + return trim(preg_replace('/\w{3}(\d)?:\d{4,5}/i', '', $raw_name)); } - private static function map_conn(string $server_name, string $vpn_id, array $conn): array { return [ @@ -567,8 +592,8 @@ class PfzOpenVpn { $servers = PfEnv::openvpn_get_active_servers(); $sk_servers = PfEnv::openvpn_get_active_servers("p2p"); - $servers = array_merge($servers, $sk_servers); - return ($servers); + + return array_merge($servers, $sk_servers); } } @@ -587,17 +612,22 @@ class PfzCommands public static function gw_value($gw, $value_key) { $gws = PfEnv::return_gateways_status(true); - if (array_key_exists($gw, $gws)) { - $value = $gws[$gw][$value_key]; - if ($value_key == "status") { - //Issue #70: Gateway Forced Down - if ($gws[$gw]["substatus"] <> "none") - $value = $gws[$gw]["substatus"]; - $value = self::get_value_mapping("gateway.status", $value); - } - echo $value; + $is_known_gw = array_key_exists($gw, $gws); + if (!$is_known_gw) { + return; } + + $value = $gws[$gw][$value_key]; + if ($value_key != "status") { + echo $value; + return; + } + + // Issue #70: Gateway Forced Down + $v = ($gws[$gw]["substatus"] != "none") ? $gws[$gw]["substatus"] : $value; + + echo self::get_value_mapping("gateway.status", $v); } public static function gw_status() @@ -699,7 +729,6 @@ class PfzCommands } } - public static function temperature($sensorid) { exec("sysctl '$sensorid' | cut -d ':' -f 2", $value, $code); @@ -711,56 +740,50 @@ class PfzCommands echo trim($value[0]); } - public static function carp_status($echo = true): int + public static function carp_status($echo_result = true): int { - //Detect CARP Status + // Detect CARP Status $config = PfEnv::cfg(); - $status_return = 0; - $status = PfEnv::get_carp_status(); + $carp_status = PfEnv::get_carp_status(); $carp_detected_problems = PfEnv::get_single_sysctl("net.inet.carp.demotion"); - //CARP is disabled - $ret = 0; - - if ($status != 0) { //CARP is enabled - - if ($carp_detected_problems != 0) { - //There's some Major Problems with CARP - $ret = 4; - if ($echo == true) echo $ret; - return $ret; - } - - $status_changed = false; - $prev_status = ""; - foreach ($config['virtualip']['vip'] as $carp) { - if ($carp['mode'] != "carp") { - continue; - } - $if_status = PfEnv::get_carp_interface_status("_vip{$carp['uniqid']}"); - - if (($prev_status != $if_status) && (empty($if_status) == false)) { //Some glitches with GUI - if ($prev_status != "") $status_changed = true; - $prev_status = $if_status; - } - } - if ($status_changed) { - //CARP Status is inconsistent across interfaces - $ret = 3; - echo 3; - } else { - if ($prev_status == "MASTER") - $ret = 1; - else - $ret = 2; - } + $is_carp_enabled = $carp_status != 0; + if (!$is_carp_enabled) { // CARP is disabled + if ($echo_result) echo CARP_STATUS_DISABLED; + return CARP_STATUS_DISABLED; } - if ($echo == true) echo $ret; - return $ret; + if ($carp_detected_problems != 0) { + // There's some Major Problems with CARP + if ($echo_result) echo CARP_STATUS_PROBLEM; + return CARP_STATUS_PROBLEM; + } + $virtual_ips = $config['virtualip']['vip']; + $just_carps = array_filter($virtual_ips, fn($virtual_ip) => $virtual_ip['mode'] != "carp"); + $status_str = array_reduce($just_carps, function ($status, $carp) { + $if_status = PfEnv::get_carp_interface_status("_vip{$carp['uniqid']}"); + + $state_differs_from_previous_interface = ($status != $if_status) && (!empty($if_status)); + if (!$state_differs_from_previous_interface) { + return $status; + } + + if ($status != "") { + return CARP_INCONSISTENT; + } + + return $if_status; + }, ""); + + $is_known_carp_status = array_key_exists($status_str, CARP_RES); + + $result = $is_known_carp_status ? CARP_RES[$status_str] : CARP_STATUS_UNKNOWN; + + return Util::result($result, $echo_result); } + // System Information public static function system($section) { @@ -789,7 +812,7 @@ class PfzCommands // If Getting "disabled" value only check item presence in config array require_once("ipsec.inc"); $config = PfEnv::cfg(); - PfEnv::init_config_arr(array('ipsec', 'phase1')); + PfEnv::init_config_arr(['ipsec', 'phase1']); $a_phase1 = &$config['ipsec']['phase1']; if ($value_key == "status") { @@ -825,29 +848,23 @@ class PfzCommands $valuecfr = explode(".", $value_key); - switch ($valuecfr[0]) { - case 'status': - $idarr = explode(".", $uniqid); - $statuskey = "state"; - if (isset($valuecfr[1])) $statuskey = $valuecfr[1]; - $value = self::get_ipsec_status($idarr[0], $idarr[1], $statuskey); - break; - case 'disabled': - $value = "0"; + $value = "0"; + if ($valuecfr[0] == 'status') { + $ids = explode(".", $uniqid); + $status_key = (isset($valuecfr[1])) ? $valuecfr[1] : "state"; + $value = self::get_ipsec_status($ids[0], $ids[1], $status_key); } - foreach ($a_phase2 as $data) { - if ($data['uniqid'] == $uniqid) { - if (array_key_exists($value_key, $data)) { - if ($value_key == 'disabled') - $value = "1"; - else - $value = self::get_value_mapping("ipsec_ph2." . $value_key, $data[$value_key], $data[$value_key]); - break; - } - } + $maybe_data = Util::array_first($a_phase2, fn($data) => $data['uniqid'] == $uniqid); + if (is_null($maybe_data) || !array_key_exists($value_key, $maybe_data)) { + return Util::result($value, true); } - echo $value; + + $result = ($value_key == 'disabled') ? + "1" : + self::get_value_mapping("ipsec_ph2." . $value_key, $maybe_data[$value_key]); + + return Util::result($result, true); } public static function dhcp($section) @@ -856,7 +873,7 @@ class PfzCommands return; } - echo PfzCommands::check_dhcp_failover(); + echo self::check_dhcp_failover(); } // File is present @@ -869,11 +886,8 @@ class PfzCommands { require_once("services.inc"); $ifdescrs = PfEnv::get_configured_interface_with_descr(true); - $ifaces = PfEnv::get_interface_arr(); - $pf_interface_name = ''; - $subvalue = false; - $ifcs = PfzDiscoveries::interface_discovery(true, true); + $ifcs = PfzDiscoveries::interfaces(); // FIXME ED Retrieve interfaces foreach ($ifcs as $if_name) { @@ -898,45 +912,38 @@ class PfzCommands // Taken from /usr/local/www/widgets/widgets/smart_status.widget.php public static function smart_status() { - foreach (PfEnv::get_smart_drive_list() as $dev) { ## for each found drive do + foreach (PfEnv::get_smart_drive_list() as $dev) { $dev_state = trim(exec("smartctl -H /dev/$dev | awk -F: '/^SMART overall-health self-assessment test result/ {print $2;exit} /^SMART Health Status/ {print $2;exit}'")); ## get SMART state from drive $is_known_state = array_key_exists($dev_state, SMART_DEV_STATUS); if (!$is_known_state) { - return SMART_ERROR; // ED This is probably a bug, status should be echoed + return Util::result(SMART_ERROR, true); } $status = SMART_DEV_STATUS[$dev_state]; if ($status !== SMART_OK) { - return $status; // ED This is probably a bug, status should be echoed + return Util::result($status, true); } } - echo SMART_OK; + return Util::result(SMART_OK, true); } public static function cert_date($value_key) { - $config = PfEnv::cfg(); - - $value = 0; - foreach (array("cert", "ca") as $cert_type) { - switch ($value_key) { - case "validFrom.max": - foreach ($config[$cert_type] as $cert) { - $certinfo = openssl_x509_parse(base64_decode($cert[PfEnv::CRT])); - if ($value == 0 or $value < $certinfo['validFrom_time_t']) $value = $certinfo['validFrom_time_t']; - } - break; - case "validTo.min": - foreach ($config[$cert_type] as $cert) { - $certinfo = openssl_x509_parse(base64_decode($cert[PfEnv::CRT])); - if ($value == 0 or $value > $certinfo['validTo_time_t']) $value = $certinfo['validTo_time_t']; - } - break; - } + if (!array_key_exists($value_key, CERT_VK_TO_FIELD)) { + return Util::result(0, true); } - echo $value; + + $field = CERT_VK_TO_FIELD[$value_key]; + $config = PfEnv::cfg(); + $all_certs = array_merge(...array_map(fn($cert_type) => $config[$cert_type], ["cert", "ca"])); + + return Util::result(array_reduce($all_certs, function ($value, $certificate) use ($field) { + $cert_info = openssl_x509_parse(base64_decode($certificate[PfEnv::CRT])); + + return ($value == 0 || $value < $cert_info[$field]) ? $cert_info[$field] : $value; + }, 0)); } // Testing function, for template creating purpose @@ -977,7 +984,6 @@ class PfzCommands $config = PfEnv::cfg(); PfEnv::init_config_arr(array('ipsec', 'phase1')); PfEnv::init_config_arr(array('ipsec', 'phase2')); - $a_phase2 = &$config['ipsec']['phase2']; $status = PfEnv::ipsec_list_sa(); echo "IPsec Status: \n"; print_r($status); @@ -1003,22 +1009,19 @@ class PfzCommands private static function get_openvpn_server_uservalue_($unique_id, $value_key, $default = "") { $unique_id = Util::replace_special_chars($unique_id, true); - $atpos = strpos($unique_id, '+'); - $server_id = substr($unique_id, 0, $atpos); - $user_id = substr($unique_id, $atpos + 1); + + list($server_id, $user_id) = explode("+", $unique_id); $servers = PfzOpenVpn::get_all_openvpn_servers(); - foreach ($servers as $server) { - if ($server['vpnid'] == $server_id) { - foreach ($server['conns'] as $conn) { - if ($conn['common_name'] == $user_id) { - $value = $conn[$value_key]; - } - } - } + $maybe_server = Util::array_first($servers, fn($server) => $server['vpnid'] == $server_id); + + if (!$maybe_server) { + return $default; } - return ($value == "") ? $default : $value; + $maybe_conn = Util::array_first($maybe_server["conns"], fn($conn) => ($conn['common_name'] == $user_id)); + + return $maybe_conn[$value_key] ?: $default; } private static function get_server_value($maybe_server, $value_key) @@ -1033,14 +1036,12 @@ class PfzCommands return $raw_value == "" ? "server_user_listening" : $raw_value; } - if ($maybe_server["mode"] == "p2p_tls") { - // For p2p_tls, ensure we have one client, and return up if it's the case - if ($raw_value == "") { - $has_at_least_one_connection = - is_array($maybe_server["conns"]) && count($maybe_server["conns"]) > 0; + // For p2p_tls, ensure we have one client, and return up if it's the case + if ($maybe_server["mode"] == "p2p_tls" && $raw_value == "") { + $has_at_least_one_connection = + is_array($maybe_server["conns"]) && count($maybe_server["conns"]) > 0; - return $has_at_least_one_connection ? "up" : "down"; - } + return $has_at_least_one_connection ? "up" : "down"; } return $raw_value; @@ -1130,23 +1131,24 @@ class PfzCommands // DHCP Checks (copy of status_dhcp_leases.php, waiting for pfsense 2.5) private static function remove_duplicates($array, $field): array { + $cmp = []; + $new = []; + foreach ($array as $sub) { $cmp[] = $sub[$field]; } + $unique = array_unique(array_reverse($cmp, true)); foreach ($unique as $k => $rien) { $new[] = $array[$k]; } + return $new; } // Get DHCP Arrays (copied from status_dhcp_leases.php, waiting for pfsense 2.5, in order to use system_get_dhcpleases();) private static function get_dhcp($value_key) { - - - $leasesfile = "{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases"; - $awk = "/usr/bin/awk"; /* this pattern sticks comments into a single array item */ $cleanpattern = "'{ gsub(\"#.*\", \"\");} { gsub(\";\", \"\"); print;}'"; @@ -1359,7 +1361,6 @@ function main($arguments) $command = strtolower($arguments[1]); $parameters = array_slice($arguments, 2); - if ($command == "help") { print_r(COMMAND_HANDLERS); exit; From ce28765be5580afb452cf15b006912f4f018feef Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sat, 19 Feb 2022 00:30:25 +0100 Subject: [PATCH 28/93] Simplify and clean miscellaneous methods --- pfsense_zbx.php | 102 ++++++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 4753528..0688833 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -10,28 +10,20 @@ require_once("config.inc"); require_once('globals.inc'); require_once('functions.inc'); require_once("util.inc"); - -// For Interfaces Discovery require_once('interfaces.inc'); - -// For OpenVPN Discovery require_once('openvpn.inc'); - -// For Service Discovery require_once("service-utils.inc"); - -// For System require_once('pkg-utils.inc'); -//Some Useful defines -define('SPEEDTEST_INTERVAL', 8); // Speedtest Interval (in hours) - define("COMMAND_HANDLERS", build_method_lookup(PfzCommands::class)); -// Argument parsers for Discovery define('DISCOVERY_SECTION_HANDLERS', build_method_lookup(PfzDiscoveries::class)); -define("VALUE_MAPPINGS", [ +define('SERVICES_VALUES', build_method_lookup(PfzServices::class)); + +const SPEEDTEST_INTERVAL = 8; // Speedtest Interval (in hours) + +const VALUE_MAPPINGS = [ "openvpn.server.status" => [ "down" => "0", "up" => "1", @@ -80,12 +72,12 @@ define("VALUE_MAPPINGS", [ "established" => 1, "connecting" => 2, "installed" => 1, - "rekeyed" => 2]]); + "rekeyed" => 2]]; -define("CERT_VK_TO_FIELD", [ +const CERT_VK_TO_FIELD = [ "validFrom.max" => "validFrom_time_t", "validTo.min" => "validTo_time_t", -]); +]; const SMART_DEV_PASSED = "PASSED"; const SMART_DEV_OK = "OK"; @@ -115,23 +107,31 @@ const CARP_RES = [ CARP_MASTER => CARP_STATUS_OK ]; +class PfzServices +{ + public static function enabled($service, $name, $short_name): int + { + return Util::b2int(PfEnv::is_service_enabled($short_name)); + } -define("SERVICES_VALUES", [ - "status" => function ($service) { + public static function name($service, string $name) + { + echo $name; + } + + public static function status(string $service) : int + { $status = PfEnv::get_service_status($service); return ($status == "") ? 0 : $status; - }, - "name" => function ($service, $name) { - echo $name; - }, - "enabled" => function ($service, $name, $short_name) { - return Util::b2int(PfEnv::is_service_enabled($short_name)); - }, - "run_on_carp_slave" => function ($service, $name, $short_name, $carpcfr, $stopped_on_carp_slave) { + + } + + public static function run_on_carp_slave($service, $name, $short_name, $carpcfr, $stopped_on_carp_slave): int + { return Util::b2int(in_array($carpcfr, $stopped_on_carp_slave)); } -]); +} // Abstract undefined symbols and globals from code class PfEnv @@ -860,9 +860,9 @@ class PfzCommands return Util::result($value, true); } - $result = ($value_key == 'disabled') ? - "1" : - self::get_value_mapping("ipsec_ph2." . $value_key, $maybe_data[$value_key]); + $result = ($value_key != 'disabled') ? + self::get_value_mapping("ipsec_ph2." . $value_key, $maybe_data[$value_key]) : + "1"; return Util::result($result, true); } @@ -1055,35 +1055,38 @@ class PfzCommands PfEnv::init_config_arr(array('ipsec', 'phase1')); $a_phase1 = &$config['ipsec']['phase1']; - $conmap = []; - foreach ($a_phase1 as $ph1ent) { + + $conmap = array_reduce($a_phase1, function ($p, $ph1ent) { + $ike_id = $ph1ent['ikeid']; if (function_exists('get_ipsecifnum')) { - if (PfEnv::get_ipsecifnum($ph1ent['ikeid'], 0)) { - $cname = "con" . PfEnv::get_ipsecifnum($ph1ent['ikeid'], 0); - } else { - $cname = "con{$ph1ent['ikeid']}00000"; - } + $id_name = (PfEnv::get_ipsecifnum($ike_id, 0)); + + $cname = $id_name ? "con$id_name" : "con{$ike_id}00000"; } else { $cname = ipsec_conid($ph1ent); } - $conmap[$cname] = $ph1ent['ikeid']; - } + + return [ + ...$p, + $cname => $ph1ent[$ike_id], + ]; + }, []); $status = PfEnv::ipsec_list_sa(); $ipsecconnected = []; $carp_status = self::carp_status(false); - //Phase-Status match borrowed from status_ipsec.php + // Phase-Status match borrowed from status_ipsec.php if (is_array($status)) { - foreach ($status as $l_ikeid => $ikesa) { + foreach ($status as $ikesa) { + + $con_id = isset($ikesa['con-id']) ? + substr($ikesa['con-id'], 3) : + filter_var($ike_id, FILTER_SANITIZE_NUMBER_INT); - if (isset($ikesa['con-id'])) { - $con_id = substr($ikesa['con-id'], 3); - } else { - $con_id = filter_var($ike_id, FILTER_SANITIZE_NUMBER_INT); - } $con_name = "con" . $con_id; + if ($ikesa['version'] == 1) { $ph1idx = $conmap[$con_name]; $ipsecconnected[$ph1idx] = $ph1idx; @@ -1096,16 +1099,15 @@ class PfzCommands $ipsecconnected[$con_id] = $ph1idx = $con_id; } } + if ($ph1idx == $ike_id) { if ($req_id != -1) { // Asking for Phase2 Status Value foreach ($ikesa['child-sas'] as $childsas) { if ($childsas['reqid'] == $req_id) { + //if state is rekeyed go on + $tmp_value = $childsas[$value_key]; if (strtolower($childsas['state']) == 'rekeyed') { - //if state is rekeyed go on - $tmp_value = $childsas[$value_key]; - } else { - $tmp_value = $childsas[$value_key]; break; } } From 6cad4289009010aed09b612d00a038129e0a738d Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sat, 19 Feb 2022 00:44:07 +0100 Subject: [PATCH 29/93] Fix CARP code inconsistency --- pfsense_zbx.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 0688833..e60999e 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -119,7 +119,7 @@ class PfzServices echo $name; } - public static function status(string $service) : int + public static function status(string $service): int { $status = PfEnv::get_service_status($service); @@ -511,7 +511,8 @@ class PfzDiscoveries $has_public_ip = filter_var( $iface_info_ext["ipaddr"], - FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE); + FILTER_VALIDATE_IP, + FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE); $is_vpn = strpos($iface_info_ext["if"], "ovpn") !== false; return ($has_gw || $has_public_ip) && !$is_vpn; @@ -742,21 +743,18 @@ class PfzCommands public static function carp_status($echo_result = true): int { - // Detect CARP Status $config = PfEnv::cfg(); $carp_status = PfEnv::get_carp_status(); $carp_detected_problems = PfEnv::get_single_sysctl("net.inet.carp.demotion"); $is_carp_enabled = $carp_status != 0; if (!$is_carp_enabled) { // CARP is disabled - if ($echo_result) echo CARP_STATUS_DISABLED; - return CARP_STATUS_DISABLED; + return Util::result(CARP_STATUS_DISABLED, $echo_result); } if ($carp_detected_problems != 0) { // There's some Major Problems with CARP - if ($echo_result) echo CARP_STATUS_PROBLEM; - return CARP_STATUS_PROBLEM; + return Util::result(CARP_STATUS_PROBLEM, $echo_result); } $virtual_ips = $config['virtualip']['vip']; From de6d82dafc4c5dc0d63633520d191fc7d6215511 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sat, 19 Feb 2022 11:34:32 +0100 Subject: [PATCH 30/93] Simplify Ipsec status retrieval --- pfsense_zbx.php | 102 +++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 53 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index e60999e..d53d1e2 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1045,17 +1045,35 @@ class PfzCommands return $raw_value; } - private static function get_ipsec_status($ike_id, $req_id = -1, $value_key = 'state') + private static function get_ipsec_status($ike_id, $req_id = -1, $value_key = "state") { - require_once("ipsec.inc"); + PfEnv::init_config_arr(array("ipsec", "phase1")); + + $result = ""; + + $process_result = function ($vk, $r) { + if ($vk != "state") { + return $r; + } + + $v = self::get_value_mapping("ipsec.state", strtolower($r)); + + $carp_status = self::carp_status(false); + + return ($carp_status != 0) ? $v + (10 * ($carp_status - 1)) : $v; + }; + + $ipsec_list_sa = PfEnv::ipsec_list_sa(); + if (!is_array($ipsec_list_sa)) { + return $process_result($value_key, $result); + } + $config = PfEnv::cfg(); - PfEnv::init_config_arr(array('ipsec', 'phase1')); - $a_phase1 = &$config['ipsec']['phase1']; + $connection_map = array_reduce($config["ipsec"]["phase1"], function ($p, $ph1ent) { + $ike_id = $ph1ent["ikeid"]; - $conmap = array_reduce($a_phase1, function ($p, $ph1ent) { - $ike_id = $ph1ent['ikeid']; if (function_exists('get_ipsecifnum')) { $id_name = (PfEnv::get_ipsecifnum($ike_id, 0)); @@ -1070,62 +1088,40 @@ class PfzCommands ]; }, []); - $status = PfEnv::ipsec_list_sa(); - $ipsecconnected = []; - - $carp_status = self::carp_status(false); - // Phase-Status match borrowed from status_ipsec.php - if (is_array($status)) { - foreach ($status as $ikesa) { + $maybe_ike_sa = Util::array_first($ipsec_list_sa, function ($ike_sa) use ($ike_id, $connection_map) { + $con_id = isset($ike_sa["con-id"]) ? + substr($ike_sa["con-id"], 3) : + filter_var($ike_id, FILTER_SANITIZE_NUMBER_INT); - $con_id = isset($ikesa['con-id']) ? - substr($ikesa['con-id'], 3) : - filter_var($ike_id, FILTER_SANITIZE_NUMBER_INT); + $con_name = "con$con_id"; - $con_name = "con" . $con_id; + $is_version_1 = $ike_sa['version'] == 1; + $is_split_connection = !$is_version_1 && !PfEnv::ipsec_ikeid_used($con_id); - if ($ikesa['version'] == 1) { - $ph1idx = $conmap[$con_name]; - $ipsecconnected[$ph1idx] = $ph1idx; - } else { - if (!PfEnv::ipsec_ikeid_used($con_id)) { - // probably a v2 with split connection then - $ph1idx = $conmap[$con_name]; - $ipsecconnected[$ph1idx] = $ph1idx; - } else { - $ipsecconnected[$con_id] = $ph1idx = $con_id; - } - } + $ph1idx = ($is_version_1 || $is_split_connection) ? $connection_map[$con_name] : $con_id; - if ($ph1idx == $ike_id) { - if ($req_id != -1) { - // Asking for Phase2 Status Value - foreach ($ikesa['child-sas'] as $childsas) { - if ($childsas['reqid'] == $req_id) { - //if state is rekeyed go on - $tmp_value = $childsas[$value_key]; - if (strtolower($childsas['state']) == 'rekeyed') { - break; - } - } - } - } else { - $tmp_value = $ikesa[$value_key]; - } + return $ph1idx == $ike_id; + }); - break; - } + if (!$maybe_ike_sa) { + return $process_result($value_key, $result); + } + + $just_matching_child_sas = + array_filter($maybe_ike_sa["child-sas"], fn($child_sa) => ($child_sa["reqid"] == $req_id)); + + // Asking for Phase2 Status Value + foreach ($just_matching_child_sas as $child_sa) { + $result = $child_sa[$value_key]; + + // If state is rekeyed go on + if (strtolower($child_sa["state"]) == "rekeyed") { + break; } } - if ($value_key == "state") { - $v = self::get_value_mapping('ipsec.state', strtolower($tmp_value)); - - return ($carp_status != 0) ? $v + (10 * ($carp_status - 1)) : $v; - } - - return $tmp_value; + return $process_result($value_key, $result); } // DHCP Checks (copy of status_dhcp_leases.php, waiting for pfsense 2.5) From 3b0cd404a4aecf625806ec5453e3c1cfb4c2ef07 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sat, 19 Feb 2022 11:50:54 +0100 Subject: [PATCH 31/93] Define namespace and require everything at top --- pfsense_zbx.php | 49 ++++++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index d53d1e2..f3f4144 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1,19 +1,25 @@ * This program is licensed under Apache 2.0 License */ + +namespace RBicelli\Pfz; + require_once("config.inc"); -require_once('globals.inc'); -require_once('functions.inc'); -require_once("util.inc"); -require_once('interfaces.inc'); -require_once('openvpn.inc'); +require_once("functions.inc"); +require_once("globals.inc"); +require_once("interfaces.inc"); +require_once("ipsec.inc"); +require_once("openvpn.inc"); +require_once("pkg-utils.inc"); require_once("service-utils.inc"); -require_once('pkg-utils.inc'); +require_once("services.inc"); +require_once("system.inc"); +require_once("util.inc"); define("COMMAND_HANDLERS", build_method_lookup(PfzCommands::class)); @@ -21,7 +27,7 @@ define('DISCOVERY_SECTION_HANDLERS', build_method_lookup(PfzDiscoveries::class)) define('SERVICES_VALUES', build_method_lookup(PfzServices::class)); -const SPEEDTEST_INTERVAL = 8; // Speedtest Interval (in hours) +const SPEEDTEST_INTERVAL_HOURS = 8; const VALUE_MAPPINGS = [ "openvpn.server.status" => [ @@ -270,7 +276,7 @@ class PfEnv class Util { - public static function array_first(array $haystack, Callback $match) + public static function array_first(array $haystack, \Closure $match) { foreach ($haystack as $needle) { if ($match($needle)) { @@ -288,7 +294,7 @@ class Util public static function replace_special_chars(string $input, bool $decode = false): string { - $special_chars = explode(",", ",',\",`,*,?,[,],{,},~,$,!,&,;,(,),<,>,|,#,@,0x0a"); + $special_chars = explode("", "'\"`*?[]{}~$!&;()<>|#@\n"); $result = $input; @@ -409,7 +415,6 @@ class PfzDiscoveries public static function ipsec_ph1() { - require_once("ipsec.inc"); $config = PfEnv::cfg(); PfEnv::init_config_arr(array('ipsec', 'phase1')); $a_phase1 = &$config['ipsec']['phase1']; @@ -422,8 +427,6 @@ class PfzDiscoveries public static function ipsec_ph2() { - require_once("ipsec.inc"); - $config = PfEnv::cfg(); PfEnv::init_config_arr(array('ipsec', 'phase2')); $a_phase2 = &$config['ipsec']['phase2']; @@ -440,7 +443,6 @@ class PfzDiscoveries public static function dhcpfailover() { // System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait - require_once("system.inc"); $leases = PfEnv::system_get_dhcpleases(); self::print_json(array_map(fn($data) => [ @@ -566,7 +568,7 @@ class PfzSpeedtest // Sleep random delay in order to avoid problem when 2 pfSense on the same Internet line sleep(rand(1, 90)); - if ((time() - filemtime($filename) > SPEEDTEST_INTERVAL * 3600) || (file_exists($filename) == false)) { + if ((time() - filemtime($filename) > SPEEDTEST_INTERVAL_HOURS * 3600) || (file_exists($filename) == false)) { return; } @@ -808,7 +810,6 @@ class PfzCommands { // Get Value from IPsec Phase 1 Configuration // If Getting "disabled" value only check item presence in config array - require_once("ipsec.inc"); $config = PfEnv::cfg(); PfEnv::init_config_arr(['ipsec', 'phase1']); $a_phase1 = &$config['ipsec']['phase1']; @@ -839,7 +840,6 @@ class PfzCommands public static function ipsec_ph2($uniqid, $value_key) { - require_once("ipsec.inc"); $config = PfEnv::cfg(); PfEnv::init_config_arr(array('ipsec', 'phase2')); $a_phase2 = &$config['ipsec']['phase2']; @@ -882,7 +882,6 @@ class PfzCommands public static function speedtest_cron() { - require_once("services.inc"); $ifdescrs = PfEnv::get_configured_interface_with_descr(true); $ifcs = PfzDiscoveries::interfaces(); // FIXME ED Retrieve interfaces @@ -978,7 +977,6 @@ class PfzCommands echo "IPsec: \n"; - require_once("ipsec.inc"); $config = PfEnv::cfg(); PfEnv::init_config_arr(array('ipsec', 'phase1')); PfEnv::init_config_arr(array('ipsec', 'phase2')); @@ -999,7 +997,6 @@ class PfzCommands //Packages echo "Packages: \n"; - require_once("pkg-utils.inc"); $installed_packages = PfEnv::get_pkg_info('all', false, true); print_r($installed_packages); } @@ -1047,7 +1044,6 @@ class PfzCommands private static function get_ipsec_status($ike_id, $req_id = -1, $value_key = "state") { - require_once("ipsec.inc"); PfEnv::init_config_arr(array("ipsec", "phase1")); $result = ""; @@ -1295,7 +1291,7 @@ class PfzCommands } - private static function check_dhcp_failover() + private static function check_dhcp_failover(): int { // Check DHCP Failover Status // Returns number of failover pools which state is not normal or @@ -1305,9 +1301,8 @@ class PfzCommands return count(array_filter($failover, fn($f) => ($f["mystate"] != "normal") || ($f["mystate"] != $f["peerstate"]))); } - private static function get_outdated_packages() + private static function get_outdated_packages(): int { - require_once("pkg-utils.inc"); $installed_packages = PfEnv::get_pkg_info("all", false, true); @@ -1340,14 +1335,14 @@ class PfzCommands function build_method_lookup(string $clazz): array { try { - $reflector = new ReflectionClass($clazz); + $reflector = new \ReflectionClass($clazz); $all_methods = $reflector->getMethods(); $commands = array_filter($all_methods, fn($method) => $method->isStatic() && $method->isPublic()); - return array_map(fn(ReflectionMethod $method) => $method->getName(), $commands); - } catch (Exception $e) { + return array_map(fn(\ReflectionMethod $method) => $method->getName(), $commands); + } catch (\Exception $e) { return []; } } From f47110d1b549d4fc70cc107ae9a27738ac4935da Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sat, 19 Feb 2022 11:58:04 +0100 Subject: [PATCH 32/93] Simplify switch statement in dhcp --- pfsense_zbx.php | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index f3f4144..9c51d73 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1277,18 +1277,10 @@ class PfzCommands asort($pools); } - switch ($value_key) { - case "pools": - return $pools; - break; - case "failover": - return $failover; - break; - case "leases": - default: - return $leases; - } + $rs = compact("pools", "failover", "leases"); + $is_known_value_key = array_key_exists($value_key, $rs); + return ($is_known_value_key) ? $rs[$value_key] : $leases; } private static function check_dhcp_failover(): int From 723cb102638c0d91307a8bcfb1d064a61cae0b3f Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sat, 19 Feb 2022 12:16:47 +0100 Subject: [PATCH 33/93] Fix Speedtest methods --- pfsense_zbx.php | 55 +++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 9c51d73..983d60a 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -28,6 +28,7 @@ define('DISCOVERY_SECTION_HANDLERS', build_method_lookup(PfzDiscoveries::class)) define('SERVICES_VALUES', build_method_lookup(PfzServices::class)); const SPEEDTEST_INTERVAL_HOURS = 8; +const SPEEDTEST_INTERVAL_SECONDS = SPEEDTEST_INTERVAL_HOURS * 3600; const VALUE_MAPPINGS = [ "openvpn.server.status" => [ @@ -531,12 +532,11 @@ class PfzDiscoveries class PfzSpeedtest { - // Interface Speedtest - public static function interface_speedtest_value($if_name, $value) + public static function interface_value($if_name, $value) { list($tv0, $tv1) = explode(".", $value); - $filename = "/tmp/speedtest-$if_name"; + $filename = self::if_filename($if_name); if (!file_exists($filename)) { return; } @@ -549,49 +549,42 @@ class PfzSpeedtest echo empty($tv1) ? $speedtest_data[$value] : $speedtest_data[$tv0][$tv1]; } - // Installs a cron job for speedtests - public static function speedtest_cron_install($enable = true) + public static function cron_install($enable = true) { - //Install Cron Job $command = "/usr/local/bin/php " . __FILE__ . " speedtest_cron"; - PfEnv::install_cron_job($command, $enable, $minute = "*/15", "*", "*", "*", "*", "root", true); + PfEnv::install_cron_job($command, $enable, "*/15", "*", "*", "*", "*", "root", true); } - public static function speedtest_exec($if_name, $ip_address) + public static function exec($if_name, $ip_address) { - - $filename = "/tmp/speedtest-$if_name"; - $filetemp = "$filename.tmp"; - $filerun = "/tmp/speedtest-run"; + $output_file_path = self::if_filename($if_name); + $tmp_file_path = tempnam(sys_get_temp_dir(), ""); // Issue #82 // Sleep random delay in order to avoid problem when 2 pfSense on the same Internet line sleep(rand(1, 90)); - if ((time() - filemtime($filename) > SPEEDTEST_INTERVAL_HOURS * 3600) || (file_exists($filename) == false)) { + $is_output_file_older_than_interval = + file_exists($output_file_path) && + (time() - filemtime($output_file_path) > SPEEDTEST_INTERVAL_SECONDS); + if (!$is_output_file_older_than_interval) { return; } - // file is older than SPEEDTEST_INTERVAL - if ((time() - filemtime($filerun) > 180)) { - @unlink($filerun); - } - - if (file_exists($filerun)) { - return; - } - - touch($filerun); - $st_command = "/usr/local/bin/speedtest --source $ip_address --json > $filetemp"; + $st_command = "/usr/local/bin/speedtest --source $ip_address --json > $tmp_file_path"; exec($st_command); - rename($filetemp, $filename); - @unlink($filerun); + rename($tmp_file_path, $output_file_path); + } + + private static function if_filename($if_name): string + { + return implode(DIRECTORY_SEPARATOR, [sys_get_temp_dir(), "speedtest-$if_name"]); } } class PfzOpenVpn { - public static function get_all_openvpn_servers() + public static function get_all_openvpn_servers(): array { $servers = PfEnv::openvpn_get_active_servers(); $sk_servers = PfEnv::openvpn_get_active_servers("p2p"); @@ -643,8 +636,8 @@ class PfzCommands public static function if_speedtest_value($if_name, $value) { - PfzSpeedtest::speedtest_cron_install(); - PfzSpeedtest::interface_speedtest_value($if_name, $value); + PfzSpeedtest::cron_install(); + PfzSpeedtest::interface_value($if_name, $value); } public static function openvpn_servervalue($server_id, $value_key) @@ -896,13 +889,13 @@ class PfzCommands } } - PfzSpeedtest::speedtest_exec($if_name, $if_info['ipaddr']); + PfzSpeedtest::exec($if_name, $if_info['ipaddr']); } } public static function cron_cleanup() { - PfzSpeedtest::speedtest_cron_install(false); + PfzSpeedtest::cron_install(false); } // S.M.A.R.T Status From 6a3f5ad0404546b4072e6659ac306772032a80b7 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sat, 19 Feb 2022 12:25:40 +0100 Subject: [PATCH 34/93] Simplify SMART status retrieval --- pfsense_zbx.php | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 983d60a..516798c 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -902,21 +902,20 @@ class PfzCommands // Taken from /usr/local/www/widgets/widgets/smart_status.widget.php public static function smart_status() { - foreach (PfEnv::get_smart_drive_list() as $dev) { - $dev_state = trim(exec("smartctl -H /dev/$dev | awk -F: '/^SMART overall-health self-assessment test result/ {print $2;exit} -/^SMART Health Status/ {print $2;exit}'")); ## get SMART state from drive - $is_known_state = array_key_exists($dev_state, SMART_DEV_STATUS); - if (!$is_known_state) { - return Util::result(SMART_ERROR, true); - } + $dev_states = array_map( + fn($dev) => trim(exec("smartctl -H /dev/$dev | awk -F: '/^SMART overall-health self-assessment test result/ {print $2;exit} +/^SMART Health Status/ {print $2;exit}'")), + PfEnv::get_smart_drive_list()); - $status = SMART_DEV_STATUS[$dev_state]; - if ($status !== SMART_OK) { - return Util::result($status, true); - } - } + $maybe_not_ok = Util::array_first($dev_states, function ($dev_state) { + $is_ok = + array_key_exists($dev_state, SMART_DEV_STATUS) && + SMART_DEV_STATUS[$dev_state] == SMART_OK; - return Util::result(SMART_OK, true); + return !$is_ok; + }); + + return Util::result($maybe_not_ok ?: SMART_OK, true); } public static function cert_date($value_key) From 49e708b09320ffd007b57a1dfff53ad8322a0201 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sat, 19 Feb 2022 12:40:07 +0100 Subject: [PATCH 35/93] Fix Speedtest issue --- pfsense_zbx.php | 74 ++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 41 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 516798c..8025f1c 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -320,6 +320,36 @@ class Util } } +class PfzInterfaces +{ + public static function retrieve_wan_interfaces(): array + { + $if_descriptions = PfEnv::get_configured_interface_with_descr(true); + + $interfaces = array_map(function ($interface) { + list ($if_name, $description) = $interface; + + return [ + ...PfEnv::get_interface_info($if_name), + "description" => $description, + ]; + }, array_map(null, array_keys($if_descriptions), array_values($if_descriptions))); + + return array_filter($interfaces, function ($iface_info_ext) { + $has_gw = array_key_exists("gateway", $iface_info_ext); + // Issue #81 - https://stackoverflow.com/a/13818647/15093007 + $has_public_ip = + filter_var( + $iface_info_ext["ipaddr"], + FILTER_VALIDATE_IP, + FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE); + $is_vpn = strpos($iface_info_ext["if"], "ovpn") !== false; + + return ($has_gw || $has_public_ip) && !$is_vpn; + }); + } +} + class PfzDiscoveries { public static function gw() @@ -491,42 +521,17 @@ class PfzDiscoveries private static function discover_interface($is_wan = false) { - if (!$is_wan) { self::print_json([]); return; } - $if_descriptions = PfEnv::get_configured_interface_with_descr(true); - - $interfaces = array_map(function ($interface) { - list ($if_name, $description) = $interface; - - return [ - ...PfEnv::get_interface_info($if_name), - "description" => $description, - ]; - }, array_map(null, array_keys($if_descriptions), array_values($if_descriptions))); - - $wan_interfaces = array_filter($interfaces, function ($iface_info_ext) { - $has_gw = array_key_exists("gateway", $iface_info_ext); - // Issue #81 - https://stackoverflow.com/a/13818647/15093007 - $has_public_ip = - filter_var( - $iface_info_ext["ipaddr"], - FILTER_VALIDATE_IP, - FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE); - $is_vpn = strpos($iface_info_ext["if"], "ovpn") !== false; - - return ($has_gw || $has_public_ip) && !$is_vpn; - }); - self::print_json(array_map(function ($hwif) { return [ "{#IFNAME}" => $hwif['hwif'], "{#IFDESCR}" => $hwif["description"], ]; - }, $wan_interfaces)); + }, PfzInterfaces::retrieve_wan_interfaces())); } } @@ -875,21 +880,8 @@ class PfzCommands public static function speedtest_cron() { - $ifdescrs = PfEnv::get_configured_interface_with_descr(true); - - $ifcs = PfzDiscoveries::interfaces(); // FIXME ED Retrieve interfaces - - foreach ($ifcs as $if_name) { - - foreach ($ifdescrs as $ifn => $ifd) { - $if_info = PfEnv::get_interface_info($ifn); - if ($if_info['hwif'] == $if_name) { - $pf_interface_name = $ifn; - break; - } - } - - PfzSpeedtest::exec($if_name, $if_info['ipaddr']); + foreach (PfzInterfaces::retrieve_wan_interfaces() as $if_info) { + PfzSpeedtest::exec($if_info["hwif"], $if_info["ipaddr"]); } } From 25b95784ca5fb37399cdd3c4f1937045b4100d81 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sat, 19 Feb 2022 12:46:31 +0100 Subject: [PATCH 36/93] Add method for flattening array --- pfsense_zbx.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 8025f1c..f73cc8d 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -288,6 +288,11 @@ class Util return null; } + public static function array_flatten(array $multi_dimensional_array): array + { + return array_merge(...$multi_dimensional_array); + } + public static function b2int(bool $b): int { return (int)$b; @@ -413,7 +418,7 @@ class PfzDiscoveries $servers_with_relevant_mode, fn($server) => is_array($server["conns"])); - self::print_json(array_merge(...array_map(fn($s) => self::map_server($s), $servers_with_conns))); + self::print_json(Util::array_flatten(array_map(fn($s) => self::map_server($s), $servers_with_conns))); } public static function openvpn_client() @@ -918,7 +923,7 @@ class PfzCommands $field = CERT_VK_TO_FIELD[$value_key]; $config = PfEnv::cfg(); - $all_certs = array_merge(...array_map(fn($cert_type) => $config[$cert_type], ["cert", "ca"])); + $all_certs = Util::array_flatten(array_map(fn($cert_type) => $config[$cert_type], ["cert", "ca"])); return Util::result(array_reduce($all_certs, function ($value, $certificate) use ($field) { $cert_info = openssl_x509_parse(base64_decode($certificate[PfEnv::CRT])); From 5ccbd7fa59adc1526279ee184cd52a922a7980d5 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sat, 19 Feb 2022 13:35:27 +0100 Subject: [PATCH 37/93] Fix magic method invocation --- pfsense_zbx.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index f73cc8d..60d851e 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -156,7 +156,7 @@ class PfEnv { $caller_function_name = debug_backtrace()[1]['function']; - return call_user_func($caller_function_name, ...func_get_args()); + return call_user_func_array($caller_function_name, ...func_get_args()); } public static function convert_friendly_interface_to_friendly_descr() From 38f73ec622fe1d2f1ae6316d67ffa646167400d9 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sat, 19 Feb 2022 13:41:55 +0100 Subject: [PATCH 38/93] Fix wan interface retrieval --- pfsense_zbx.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 60d851e..a947878 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -293,6 +293,11 @@ class Util return array_merge(...$multi_dimensional_array); } + public static function array_zip(array $keys, array $values): array + { + return array_map(null, array_keys($keys), array_values($values)); + } + public static function b2int(bool $b): int { return (int)$b; @@ -334,11 +339,11 @@ class PfzInterfaces $interfaces = array_map(function ($interface) { list ($if_name, $description) = $interface; - return [ - ...PfEnv::get_interface_info($if_name), - "description" => $description, - ]; - }, array_map(null, array_keys($if_descriptions), array_values($if_descriptions))); + return array_merge( + PfEnv::get_interface_info($if_name), + ["description" => $description], + ); + }, Util::array_zip(array_keys($if_descriptions), array_values($if_descriptions))); return array_filter($interfaces, function ($iface_info_ext) { $has_gw = array_key_exists("gateway", $iface_info_ext); From c8707e524291202bcc07431bdf6d95714df7b211 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sat, 19 Feb 2022 13:44:45 +0100 Subject: [PATCH 39/93] Fix array_zip --- pfsense_zbx.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index a947878..7b5bda4 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -295,7 +295,7 @@ class Util public static function array_zip(array $keys, array $values): array { - return array_map(null, array_keys($keys), array_values($values)); + return array_map(null, $keys, $values); } public static function b2int(bool $b): int From b9f842c9ed0df598174786f1eaf3ff3d5f29c558 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sat, 19 Feb 2022 15:28:35 +0100 Subject: [PATCH 40/93] Fix undefined variables in get_dhcp --- pfsense_zbx.php | 50 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 7b5bda4..4be1897 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -27,6 +27,14 @@ define('DISCOVERY_SECTION_HANDLERS', build_method_lookup(PfzDiscoveries::class)) define('SERVICES_VALUES', build_method_lookup(PfzServices::class)); +define("TEXT_ACTIVE", gettext("active")); +define("TEXT_DYNAMIC", gettext("dynamic")); +define("TEXT_EXPIRED", gettext("expired")); +define("TEXT_NEVER", gettext("Never")); +define("TEXT_OFFLINE", gettext("offline")); +define("TEXT_ONLINE", gettext("online")); +define("TEXT_RESERVED", gettext("reserved")); + const SPEEDTEST_INTERVAL_HOURS = 8; const SPEEDTEST_INTERVAL_SECONDS = SPEEDTEST_INTERVAL_HOURS * 3600; @@ -152,6 +160,13 @@ class PfEnv return $config; } + public static function g() + { + global $g; + + return $g; + } + private static function call_pfsense_method_with_same_name_and_arguments() { $caller_function_name = debug_backtrace()[1]['function']; @@ -1135,17 +1150,24 @@ class PfzCommands // Get DHCP Arrays (copied from status_dhcp_leases.php, waiting for pfsense 2.5, in order to use system_get_dhcpleases();) private static function get_dhcp($value_key) { + $g = PfEnv::g(); + + $leases_file = "{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases"; + $awk = "/usr/bin/awk"; /* this pattern sticks comments into a single array item */ - $cleanpattern = "'{ gsub(\"#.*\", \"\");} { gsub(\";\", \"\"); print;}'"; + $clean_pattern = "'{ gsub(\"#.*\", \"\");} { gsub(\";\", \"\"); print;}'"; /* We then split the leases file by } */ - $splitpattern = "'BEGIN { RS=\"}\";} {for (i=1; i<=NF; i++) printf \"%s \", \$i; printf \"}\\n\";}'"; + $split_pattern = "'BEGIN { RS=\"}\";} {for (i=1; i<=NF; i++) printf \"%s \", \$i; printf \"}\\n\";}'"; /* stuff the leases file in a proper format into a array by line */ - @exec("/bin/cat {$leasesfile} 2>/dev/null| {$awk} {$cleanpattern} | {$awk} {$splitpattern}", $leases_content); + @exec("/bin/cat {$leases_file} 2>/dev/null| {$awk} {$clean_pattern} | {$awk} {$split_pattern}", $leases_content); $leases_count = count($leases_content); @exec("/usr/sbin/arp -an", $rawdata); + $failover = []; + $leases = []; + $pools = []; foreach ($leases_content as $lease) { /* split the line by space */ $data = explode(" ", $lease); @@ -1173,7 +1195,7 @@ class PfzCommands continue 3; case "lease": $leases[$l]['ip'] = $data[$f + 1]; - $leases[$l]['type'] = $dynamic_string; + $leases[$l]['type'] = TEXT_DYNAMIC; $f = $f + 2; break; case "starts": @@ -1185,7 +1207,7 @@ class PfzCommands if ($data[$f + 1] == "never") { // Quote from dhcpd.leases(5) man page: // If a lease will never expire, date is never instead of an actual date. - $leases[$l]['end'] = gettext("Never"); + $leases[$l]['end'] = TEXT_NEVER; $f = $f + 1; } else { $leases[$l]['end'] = $data[$f + 2]; @@ -1208,15 +1230,15 @@ class PfzCommands case "binding": switch ($data[$f + 2]) { case "active": - $leases[$l]['act'] = $active_string; + $leases[$l]['act'] = TEXT_ACTIVE; break; case "free": - $leases[$l]['act'] = $expired_string; - $leases[$l]['online'] = $offline_string; + $leases[$l]['act'] = TEXT_EXPIRED; + $leases[$l]['online'] = TEXT_OFFLINE; break; case "backup": - $leases[$l]['act'] = $reserved_string; - $leases[$l]['online'] = $offline_string; + $leases[$l]['act'] = TEXT_RESERVED; + $leases[$l]['online'] = TEXT_OFFLINE; break; } $f = $f + 1; @@ -1231,12 +1253,10 @@ class PfzCommands break; case "hardware": $leases[$l]['mac'] = $data[$f + 2]; + $arpdata_ip = []; /* check if it's online and the lease is active */ - if (in_array($leases[$l]['ip'], $arpdata_ip)) { - $leases[$l]['online'] = $online_string; - } else { - $leases[$l]['online'] = $offline_string; - } + $leases[$l]['online'] = + (in_array($leases[$l]['ip'], $arpdata_ip)) ? TEXT_ONLINE : TEXT_OFFLINE; $f = $f + 2; break; case "client-hostname": From 3a033695ff886c0254227649a729331e1ea7e9ca Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 13:07:44 +0100 Subject: [PATCH 41/93] Remove superfluous character replacement --- pfsense_zbx.php | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 4be1897..0c1c9ac 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -318,23 +318,6 @@ class Util return (int)$b; } - public static function replace_special_chars(string $input, bool $decode = false): string - { - $special_chars = explode("", "'\"`*?[]{}~$!&;()<>|#@\n"); - - $result = $input; - - foreach ($special_chars as $idx => $plain) { - $encoded = "%%$idx%"; - - list($search, $replace) = $decode ? [$encoded, $plain] : [$plain, $encoded]; - - $result = str_replace($search, $replace, $result); - } - - return $result; - } - public static function result($result, bool $echo_result = false) { if ($echo_result) { @@ -420,7 +403,7 @@ class PfzDiscoveries $servers = PfzOpenVpn::get_all_openvpn_servers(); self::print_json(array_map(fn($server) => [ - "{#SERVER}" => $server['vpnid'], + "{#SERVER}" => $server["vpnid"], "{#NAME}" => self::sanitize_name($server["name"])], $servers)); } @@ -524,8 +507,8 @@ class PfzDiscoveries return [ "{#SERVERID}" => $vpn_id, "{#SERVERNAME}" => $server_name, - "{#UNIQUEID}" => sprintf("%s+%s", $vpn_id, Util::replace_special_chars($conn['common_name'])), - "{#USERID}" => Util::replace_special_chars($conn['common_name']), + "{#UNIQUEID}" => sprintf("%s+%s", $vpn_id, $conn["common_name"]), + "{#USERID}" => $conn["common_name"], ]; } @@ -670,11 +653,11 @@ class PfzCommands PfzSpeedtest::interface_value($if_name, $value); } - public static function openvpn_servervalue($server_id, $value_key) + public static function openvpn_servervalue(int $server_id, $value_key) { $servers = PfzOpenVpn::get_all_openvpn_servers(); - $maybe_server = Util::array_first($servers, fn($s) => $s['vpnid'] == $server_id); + $maybe_server = Util::array_first($servers, fn($s) => $s["vpnid"] == $server_id); $server_value = self::get_server_value($maybe_server, $value_key); From b206c2446e49dd5848492525ff396a6339140060 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 13:17:42 +0100 Subject: [PATCH 42/93] Use double quotes consistently --- pfsense_zbx.php | 140 ++++++++++++++++++++++++------------------------ 1 file changed, 69 insertions(+), 71 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 0c1c9ac..0aea1a5 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -23,9 +23,9 @@ require_once("util.inc"); define("COMMAND_HANDLERS", build_method_lookup(PfzCommands::class)); -define('DISCOVERY_SECTION_HANDLERS', build_method_lookup(PfzDiscoveries::class)); +define("DISCOVERY_SECTION_HANDLERS", build_method_lookup(PfzDiscoveries::class)); -define('SERVICES_VALUES', build_method_lookup(PfzServices::class)); +define("SERVICES_VALUES", build_method_lookup(PfzServices::class)); define("TEXT_ACTIVE", gettext("active")); define("TEXT_DYNAMIC", gettext("dynamic")); @@ -169,7 +169,7 @@ class PfEnv private static function call_pfsense_method_with_same_name_and_arguments() { - $caller_function_name = debug_backtrace()[1]['function']; + $caller_function_name = debug_backtrace()[1]["function"]; return call_user_func_array($caller_function_name, ...func_get_args()); } @@ -427,22 +427,22 @@ class PfzDiscoveries public static function openvpn_client() { self::print_json(array_map(fn($client) => [ - "{#CLIENT}" => $client['vpnid'], + "{#CLIENT}" => $client["vpnid"], "{#NAME}", self::sanitize_name($client["name"]), ], PfEnv::openvpn_get_active_clients())); } public static function services() { - $named_services = array_filter(PfEnv::get_services(), fn($service) => !empty($service['name'])); + $named_services = array_filter(PfEnv::get_services(), fn($service) => !empty($service["name"])); self::print_json(array_map(function ($service) { $maybe_id = Util::array_first(array_keys($service), fn($key) => in_array($key, ["id", "zone"])); $id = is_null($maybe_id) ? "" : $service[$maybe_id]; return [ - "{#SERVICE}" => str_replace(" ", "__", $service['name']) . $id, - "{#DESCRIPTION}" => $service['description'], + "{#SERVICE}" => str_replace(" ", "__", $service["name"]) . $id, + "{#DESCRIPTION}" => $service["description"], ]; }, $named_services)); } @@ -455,37 +455,37 @@ class PfzDiscoveries public static function ipsec_ph1() { $config = PfEnv::cfg(); - PfEnv::init_config_arr(array('ipsec', 'phase1')); - $a_phase1 = &$config['ipsec']['phase1']; + PfEnv::init_config_arr(array("ipsec", "phase1")); + $a_phase1 = &$config["ipsec"]["phase1"]; self::print_json(array_map(fn($data) => [ - "{#IKEID}" => $data['ikeid'], - "{#NAME}" => $data['descr'], + "{#IKEID}" => $data["ikeid"], + "{#NAME}" => $data["descr"], ], $a_phase1)); } public static function ipsec_ph2() { $config = PfEnv::cfg(); - PfEnv::init_config_arr(array('ipsec', 'phase2')); - $a_phase2 = &$config['ipsec']['phase2']; + PfEnv::init_config_arr(array("ipsec", "phase2")); + $a_phase2 = &$config["ipsec"]["phase2"]; self::print_json(array_map(fn($data) => [ - "{#IKEID}" => $data['ikeid'], - "{#NAME}" => $data['descr'], - "{#UNIQID}" => $data['uniqid'], - "{#REQID}" => $data['reqid'], - "{#EXTID}" => $data['ikeid'] . '.' . $data['reqid'], + "{#IKEID}" => $data["ikeid"], + "{#NAME}" => $data["descr"], + "{#UNIQID}" => $data["uniqid"], + "{#REQID}" => $data["reqid"], + "{#EXTID}" => $data["ikeid"] . "." . $data["reqid"], ], $a_phase2)); } public static function dhcpfailover() { - // System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait + // System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let"s wait $leases = PfEnv::system_get_dhcpleases(); self::print_json(array_map(fn($data) => [ - "{#FAILOVER_GROUP}" => str_replace(" ", "__", $data['name']), + "{#FAILOVER_GROUP}" => str_replace(" ", "__", $data["name"]), ], $leases["failover"])); } @@ -499,7 +499,7 @@ class PfzDiscoveries private static function sanitize_name(string $raw_name): string { - return trim(preg_replace('/\w{3}(\d)?:\d{4,5}/i', '', $raw_name)); + return trim(preg_replace("/\w{3}(\d)?:\d{4,5}/i", "", $raw_name)); } private static function map_conn(string $server_name, string $vpn_id, array $conn): array @@ -536,7 +536,7 @@ class PfzDiscoveries self::print_json(array_map(function ($hwif) { return [ - "{#IFNAME}" => $hwif['hwif'], + "{#IFNAME}" => $hwif["hwif"], "{#IFDESCR}" => $hwif["description"], ]; }, PfzInterfaces::retrieve_wan_interfaces())); @@ -643,7 +643,7 @@ class PfzCommands { echo implode(",", array_map( - fn($gw) => sprintf("%s.%s", $gw['name'], $gw['status']), + fn($gw) => sprintf("%s.%s", $gw["name"], $gw["status"]), PfEnv::return_gateways_status(true))); } @@ -688,7 +688,7 @@ class PfzCommands { $clients = PfEnv::openvpn_get_active_clients(); - $client = Util::array_first($clients, fn($client) => $client['vpnid'] == $client_id); + $client = Util::array_first($clients, fn($client) => $client["vpnid"] == $client_id); if (empty($client)) { return $fallback_value; @@ -761,14 +761,14 @@ class PfzCommands } if ($carp_detected_problems != 0) { - // There's some Major Problems with CARP + // There"s some Major Problems with CARP return Util::result(CARP_STATUS_PROBLEM, $echo_result); } - $virtual_ips = $config['virtualip']['vip']; - $just_carps = array_filter($virtual_ips, fn($virtual_ip) => $virtual_ip['mode'] != "carp"); + $virtual_ips = $config["virtualip"]["vip"]; + $just_carps = array_filter($virtual_ips, fn($virtual_ip) => $virtual_ip["mode"] != "carp"); $status_str = array_reduce($just_carps, function ($status, $carp) { - $if_status = PfEnv::get_carp_interface_status("_vip{$carp['uniqid']}"); + $if_status = PfEnv::get_carp_interface_status("_vip{$carp["uniqid"]}"); $state_differs_from_previous_interface = ($status != $if_status) && (!empty($if_status)); if (!$state_differs_from_previous_interface) { @@ -817,8 +817,8 @@ class PfzCommands // Get Value from IPsec Phase 1 Configuration // If Getting "disabled" value only check item presence in config array $config = PfEnv::cfg(); - PfEnv::init_config_arr(['ipsec', 'phase1']); - $a_phase1 = &$config['ipsec']['phase1']; + PfEnv::init_config_arr(["ipsec", "phase1"]); + $a_phase1 = &$config["ipsec"]["phase1"]; if ($value_key == "status") { echo PfzCommands::get_ipsec_status($ike_id); @@ -847,24 +847,24 @@ class PfzCommands public static function ipsec_ph2($uniqid, $value_key) { $config = PfEnv::cfg(); - PfEnv::init_config_arr(array('ipsec', 'phase2')); - $a_phase2 = &$config['ipsec']['phase2']; + PfEnv::init_config_arr(array("ipsec", "phase2")); + $a_phase2 = &$config["ipsec"]["phase2"]; $valuecfr = explode(".", $value_key); $value = "0"; - if ($valuecfr[0] == 'status') { + if ($valuecfr[0] == "status") { $ids = explode(".", $uniqid); $status_key = (isset($valuecfr[1])) ? $valuecfr[1] : "state"; $value = self::get_ipsec_status($ids[0], $ids[1], $status_key); } - $maybe_data = Util::array_first($a_phase2, fn($data) => $data['uniqid'] == $uniqid); + $maybe_data = Util::array_first($a_phase2, fn($data) => $data["uniqid"] == $uniqid); if (is_null($maybe_data) || !array_key_exists($value_key, $maybe_data)) { return Util::result($value, true); } - $result = ($value_key != 'disabled') ? + $result = ($value_key != "disabled") ? self::get_value_mapping("ipsec_ph2." . $value_key, $maybe_data[$value_key]) : "1"; @@ -970,14 +970,14 @@ class PfzCommands echo "IPsec: \n"; $config = PfEnv::cfg(); - PfEnv::init_config_arr(array('ipsec', 'phase1')); - PfEnv::init_config_arr(array('ipsec', 'phase2')); + PfEnv::init_config_arr(array("ipsec", "phase1")); + PfEnv::init_config_arr(array("ipsec", "phase2")); $status = PfEnv::ipsec_list_sa(); echo "IPsec Status: \n"; print_r($status); - $a_phase1 = &$config['ipsec']['phase1']; - $a_phase2 = &$config['ipsec']['phase2']; + $a_phase1 = &$config["ipsec"]["phase1"]; + $a_phase2 = &$config["ipsec"]["phase2"]; echo "IPsec Config Phase 1: \n"; print_r($a_phase1); @@ -989,24 +989,22 @@ class PfzCommands //Packages echo "Packages: \n"; - $installed_packages = PfEnv::get_pkg_info('all', false, true); + $installed_packages = PfEnv::get_pkg_info("all", false, true); print_r($installed_packages); } private static function get_openvpn_server_uservalue_($unique_id, $value_key, $default = "") { - $unique_id = Util::replace_special_chars($unique_id, true); - list($server_id, $user_id) = explode("+", $unique_id); $servers = PfzOpenVpn::get_all_openvpn_servers(); - $maybe_server = Util::array_first($servers, fn($server) => $server['vpnid'] == $server_id); + $maybe_server = Util::array_first($servers, fn($server) => $server["vpnid"] == $server_id); if (!$maybe_server) { return $default; } - $maybe_conn = Util::array_first($maybe_server["conns"], fn($conn) => ($conn['common_name'] == $user_id)); + $maybe_conn = Util::array_first($maybe_server["conns"], fn($conn) => ($conn["common_name"] == $user_id)); return $maybe_conn[$value_key] ?: $default; } @@ -1023,7 +1021,7 @@ class PfzCommands return $raw_value == "" ? "server_user_listening" : $raw_value; } - // For p2p_tls, ensure we have one client, and return up if it's the case + // For p2p_tls, ensure we have one client, and return up if it"s the case if ($maybe_server["mode"] == "p2p_tls" && $raw_value == "") { $has_at_least_one_connection = is_array($maybe_server["conns"]) && count($maybe_server["conns"]) > 0; @@ -1084,7 +1082,7 @@ class PfzCommands $con_name = "con$con_id"; - $is_version_1 = $ike_sa['version'] == 1; + $is_version_1 = $ike_sa["version"] == 1; $is_split_connection = !$is_version_1 && !PfEnv::ipsec_ikeid_used($con_id); $ph1idx = ($is_version_1 || $is_split_connection) ? $connection_map[$con_name] : $con_id; @@ -1166,35 +1164,35 @@ class PfzCommands switch ($data[$f]) { case "failover": $pools[$p]['name'] = trim($data[$f + 2], '"'); - $pools[$p]['name'] = "{$pools[$p]['name']} (" . PfEnv::convert_friendly_interface_to_friendly_descr(substr($pools[$p]['name'], 5)) . ")"; - $pools[$p]['mystate'] = $data[$f + 7]; - $pools[$p]['peerstate'] = $data[$f + 14]; - $pools[$p]['mydate'] = $data[$f + 10]; - $pools[$p]['mydate'] .= " " . $data[$f + 11]; - $pools[$p]['peerdate'] = $data[$f + 17]; - $pools[$p]['peerdate'] .= " " . $data[$f + 18]; + $pools[$p]["name"] = "{$pools[$p]["name"]} (" . PfEnv::convert_friendly_interface_to_friendly_descr(substr($pools[$p]["name"], 5)) . ")"; + $pools[$p]["mystate"] = $data[$f + 7]; + $pools[$p]["peerstate"] = $data[$f + 14]; + $pools[$p]["mydate"] = $data[$f + 10]; + $pools[$p]["mydate"] .= " " . $data[$f + 11]; + $pools[$p]["peerdate"] = $data[$f + 17]; + $pools[$p]["peerdate"] .= " " . $data[$f + 18]; $p++; $i++; continue 3; case "lease": - $leases[$l]['ip'] = $data[$f + 1]; - $leases[$l]['type'] = TEXT_DYNAMIC; + $leases[$l]["ip"] = $data[$f + 1]; + $leases[$l]["type"] = TEXT_DYNAMIC; $f = $f + 2; break; case "starts": - $leases[$l]['start'] = $data[$f + 2]; - $leases[$l]['start'] .= " " . $data[$f + 3]; + $leases[$l]["start"] = $data[$f + 2]; + $leases[$l]["start"] .= " " . $data[$f + 3]; $f = $f + 3; break; case "ends": if ($data[$f + 1] == "never") { // Quote from dhcpd.leases(5) man page: // If a lease will never expire, date is never instead of an actual date. - $leases[$l]['end'] = TEXT_NEVER; + $leases[$l]["end"] = TEXT_NEVER; $f = $f + 1; } else { - $leases[$l]['end'] = $data[$f + 2]; - $leases[$l]['end'] .= " " . $data[$f + 3]; + $leases[$l]["end"] = $data[$f + 2]; + $leases[$l]["end"] .= " " . $data[$f + 3]; $f = $f + 3; } break; @@ -1213,15 +1211,15 @@ class PfzCommands case "binding": switch ($data[$f + 2]) { case "active": - $leases[$l]['act'] = TEXT_ACTIVE; + $leases[$l]["act"] = TEXT_ACTIVE; break; case "free": - $leases[$l]['act'] = TEXT_EXPIRED; - $leases[$l]['online'] = TEXT_OFFLINE; + $leases[$l]["act"] = TEXT_EXPIRED; + $leases[$l]["online"] = TEXT_OFFLINE; break; case "backup": - $leases[$l]['act'] = TEXT_RESERVED; - $leases[$l]['online'] = TEXT_OFFLINE; + $leases[$l]["act"] = TEXT_RESERVED; + $leases[$l]["online"] = TEXT_OFFLINE; break; } $f = $f + 1; @@ -1235,20 +1233,20 @@ class PfzCommands $f = $f + 3; break; case "hardware": - $leases[$l]['mac'] = $data[$f + 2]; + $leases[$l]["mac"] = $data[$f + 2]; $arpdata_ip = []; /* check if it's online and the lease is active */ - $leases[$l]['online'] = - (in_array($leases[$l]['ip'], $arpdata_ip)) ? TEXT_ONLINE : TEXT_OFFLINE; + $leases[$l]["online"] = + (in_array($leases[$l]["ip"], $arpdata_ip)) ? TEXT_ONLINE : TEXT_OFFLINE; $f = $f + 2; break; case "client-hostname": if ($data[$f + 1] <> "") { - $leases[$l]['hostname'] = preg_replace('/"/', '', $data[$f + 1]); + $leases[$l]["hostname"] = preg_replace('/"/', "", $data[$f + 1]); } else { - $hostname = gethostbyaddr($leases[$l]['ip']); + $hostname = gethostbyaddr($leases[$l]["ip"]); if ($hostname <> "") { - $leases[$l]['hostname'] = $hostname; + $leases[$l]["hostname"] = $hostname; } } $f = $f + 1; From 8046c601c405fa754d9d0b59c7b2cbafa13520f5 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 13:32:21 +0100 Subject: [PATCH 43/93] Simplify duplicate removal method --- pfsense_zbx.php | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 0aea1a5..1d60754 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1110,22 +1110,11 @@ class PfzCommands return $process_result($value_key, $result); } - // DHCP Checks (copy of status_dhcp_leases.php, waiting for pfsense 2.5) - private static function remove_duplicates($array, $field): array + private static function remove_duplicates(array $haystack, $field): array { - $cmp = []; - $new = []; - - foreach ($array as $sub) { - $cmp[] = $sub[$field]; - } - - $unique = array_unique(array_reverse($cmp, true)); - foreach ($unique as $k => $rien) { - $new[] = $array[$k]; - } - - return $new; + return array_values(array_reduce($haystack, fn($lookup_table, $item) => array_merge( + $lookup_table, [$item[$field] => $item] + ), [])); } // Get DHCP Arrays (copied from status_dhcp_leases.php, waiting for pfsense 2.5, in order to use system_get_dhcpleases();) From 63055d5fa2c4eb1f5f73cd309abb29925e2e9887 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 13:35:27 +0100 Subject: [PATCH 44/93] Fix some style issues --- pfsense_zbx.php | 54 ++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 1d60754..d4a09d3 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -21,11 +21,11 @@ require_once("services.inc"); require_once("system.inc"); require_once("util.inc"); -define("COMMAND_HANDLERS", build_method_lookup(PfzCommands::class)); +define("COMMAND_HANDLERS", build_method_lookup(Commands::class)); -define("DISCOVERY_SECTION_HANDLERS", build_method_lookup(PfzDiscoveries::class)); +define("DISCOVERY_SECTION_HANDLERS", build_method_lookup(Discoveries::class)); -define("SERVICES_VALUES", build_method_lookup(PfzServices::class)); +define("SERVICES_VALUES", build_method_lookup(Services::class)); define("TEXT_ACTIVE", gettext("active")); define("TEXT_DYNAMIC", gettext("dynamic")); @@ -122,7 +122,7 @@ const CARP_RES = [ CARP_MASTER => CARP_STATUS_OK ]; -class PfzServices +class Services { public static function enabled($service, $name, $short_name): int { @@ -328,7 +328,7 @@ class Util } } -class PfzInterfaces +class Interfaces { public static function retrieve_wan_interfaces(): array { @@ -358,7 +358,7 @@ class PfzInterfaces } } -class PfzDiscoveries +class Discoveries { public static function gw() { @@ -400,7 +400,7 @@ class PfzDiscoveries public static function openvpn_server() { - $servers = PfzOpenVpn::get_all_openvpn_servers(); + $servers = OpenVpn::get_all_openvpn_servers(); self::print_json(array_map(fn($server) => [ "{#SERVER}" => $server["vpnid"], @@ -410,7 +410,7 @@ class PfzDiscoveries public static function openvpn_server_user() { - $servers = PfzOpenVpn::get_all_openvpn_servers(); + $servers = OpenVpn::get_all_openvpn_servers(); $servers_with_relevant_mode = array_filter( @@ -539,11 +539,11 @@ class PfzDiscoveries "{#IFNAME}" => $hwif["hwif"], "{#IFDESCR}" => $hwif["description"], ]; - }, PfzInterfaces::retrieve_wan_interfaces())); + }, Interfaces::retrieve_wan_interfaces())); } } -class PfzSpeedtest +class Speedtest { public static function interface_value($if_name, $value) { @@ -595,7 +595,7 @@ class PfzSpeedtest } } -class PfzOpenVpn +class OpenVpn { public static function get_all_openvpn_servers(): array { @@ -606,7 +606,7 @@ class PfzOpenVpn } } -class PfzCommands +class Commands { public static function discovery($section) { @@ -615,7 +615,7 @@ class PfzCommands return; } - PfzDiscoveries::{$section}(); + Discoveries::{$section}(); } public static function gw_value($gw, $value_key) @@ -649,13 +649,13 @@ class PfzCommands public static function if_speedtest_value($if_name, $value) { - PfzSpeedtest::cron_install(); - PfzSpeedtest::interface_value($if_name, $value); + Speedtest::cron_install(); + Speedtest::interface_value($if_name, $value); } public static function openvpn_servervalue(int $server_id, $value_key) { - $servers = PfzOpenVpn::get_all_openvpn_servers(); + $servers = OpenVpn::get_all_openvpn_servers(); $maybe_server = Util::array_first($servers, fn($s) => $s["vpnid"] == $server_id); @@ -821,7 +821,7 @@ class PfzCommands $a_phase1 = &$config["ipsec"]["phase1"]; if ($value_key == "status") { - echo PfzCommands::get_ipsec_status($ike_id); + echo Commands::get_ipsec_status($ike_id); return; } @@ -888,14 +888,14 @@ class PfzCommands public static function speedtest_cron() { - foreach (PfzInterfaces::retrieve_wan_interfaces() as $if_info) { - PfzSpeedtest::exec($if_info["hwif"], $if_info["ipaddr"]); + foreach (Interfaces::retrieve_wan_interfaces() as $if_info) { + Speedtest::exec($if_info["hwif"], $if_info["ipaddr"]); } } public static function cron_cleanup() { - PfzSpeedtest::cron_install(false); + Speedtest::cron_install(false); } // S.M.A.R.T Status @@ -940,7 +940,7 @@ class PfzCommands { $line = "-------------------\n"; - $ovpn_servers = PfzOpenVpn::get_all_openvpn_servers(); + $ovpn_servers = OpenVpn::get_all_openvpn_servers(); echo "OPENVPN Servers:\n"; print_r($ovpn_servers); echo $line; @@ -997,9 +997,9 @@ class PfzCommands { list($server_id, $user_id) = explode("+", $unique_id); - $servers = PfzOpenVpn::get_all_openvpn_servers(); - $maybe_server = Util::array_first($servers, fn($server) => $server["vpnid"] == $server_id); + $servers = OpenVpn::get_all_openvpn_servers(); + $maybe_server = Util::array_first($servers, fn($server) => $server["vpnid"] == $server_id); if (!$maybe_server) { return $default; } @@ -1016,7 +1016,6 @@ class PfzCommands } $raw_value = $maybe_server[$value_key]; - if (in_array($maybe_server["mode"], ["server_user", "server_tls_user", "server_tls"])) { return $raw_value == "" ? "server_user_listening" : $raw_value; } @@ -1264,7 +1263,7 @@ class PfzCommands $rs = compact("pools", "failover", "leases"); $is_known_value_key = array_key_exists($value_key, $rs); - return ($is_known_value_key) ? $rs[$value_key] : $leases; + return $is_known_value_key ? $rs[$value_key] : $leases; } private static function check_dhcp_failover(): int @@ -1334,13 +1333,12 @@ function main($arguments) } $is_known_command = in_array($command, COMMAND_HANDLERS); - if (!$is_known_command) { - PfzCommands::test(); + Commands::test(); exit; } - PfzCommands::{$command}(...$parameters); + Commands::{$command}(...$parameters); } main($argv); From 6844709bcd7f8f20f8e7b33cf3f714a0a6aa80d3 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 13:40:30 +0100 Subject: [PATCH 45/93] Fix some style and spelling issues --- pfsense_zbx.php | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index d4a09d3..74c1f27 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -9,6 +9,11 @@ namespace RBicelli\Pfz; +use Closure; +use Exception; +use ReflectionClass; +use ReflectionMethod; + require_once("config.inc"); require_once("functions.inc"); require_once("globals.inc"); @@ -292,7 +297,7 @@ class PfEnv class Util { - public static function array_first(array $haystack, \Closure $match) + public static function array_first(array $haystack, Closure $match) { foreach ($haystack as $needle) { if ($match($needle)) { @@ -481,7 +486,7 @@ class Discoveries public static function dhcpfailover() { - // System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let"s wait + // System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait $leases = PfEnv::system_get_dhcpleases(); self::print_json(array_map(fn($data) => [ @@ -710,7 +715,7 @@ class Commands $name = str_replace("__", " ", $name); // List of service which are stopped on CARP Slave. - // For now this is the best way i found for filtering out the triggers + // For now this is the best way I found for filtering out the triggers // Waiting for a way in Zabbix to use Global Regexp in triggers with items discovery $stopped_on_carp_slave = array("haproxy", "radvd", "openvpn.", "openvpn", "avahi"); @@ -761,7 +766,7 @@ class Commands } if ($carp_detected_problems != 0) { - // There"s some Major Problems with CARP + // There's some Major Problems with CARP return Util::result(CARP_STATUS_PROBLEM, $echo_result); } @@ -1020,7 +1025,7 @@ class Commands return $raw_value == "" ? "server_user_listening" : $raw_value; } - // For p2p_tls, ensure we have one client, and return up if it"s the case + // For p2p_tls, ensure we have one client, and return up if it's the case if ($maybe_server["mode"] == "p2p_tls" && $raw_value == "") { $has_at_least_one_connection = is_array($maybe_server["conns"]) && count($maybe_server["conns"]) > 0; @@ -1129,8 +1134,8 @@ class Commands /* We then split the leases file by } */ $split_pattern = "'BEGIN { RS=\"}\";} {for (i=1; i<=NF; i++) printf \"%s \", \$i; printf \"}\\n\";}'"; - /* stuff the leases file in a proper format into a array by line */ - @exec("/bin/cat {$leases_file} 2>/dev/null| {$awk} {$clean_pattern} | {$awk} {$split_pattern}", $leases_content); + /* stuff the leases file in a proper format into an array by line */ + @exec("/bin/cat $leases_file 2>/dev/null| $awk $clean_pattern | $awk $split_pattern", $leases_content); $leases_count = count($leases_content); @exec("/usr/sbin/arp -an", $rawdata); @@ -1184,16 +1189,10 @@ class Commands $f = $f + 3; } break; - case "tstp": - $f = $f + 3; - break; case "tsfp": - $f = $f + 3; - break; case "atsfp": - $f = $f + 3; - break; case "cltt": + case "tstp": $f = $f + 3; break; case "binding": @@ -1270,7 +1269,7 @@ class Commands { // Check DHCP Failover Status // Returns number of failover pools which state is not normal or - // different than peer state + // different from peer state $failover = self::get_dhcp("failover"); return count(array_filter($failover, fn($f) => ($f["mystate"] != "normal") || ($f["mystate"] != $f["peerstate"]))); @@ -1310,14 +1309,14 @@ class Commands function build_method_lookup(string $clazz): array { try { - $reflector = new \ReflectionClass($clazz); + $reflector = new ReflectionClass($clazz); $all_methods = $reflector->getMethods(); $commands = array_filter($all_methods, fn($method) => $method->isStatic() && $method->isPublic()); - return array_map(fn(\ReflectionMethod $method) => $method->getName(), $commands); - } catch (\Exception $e) { + return array_map(fn(ReflectionMethod $method) => $method->getName(), $commands); + } catch (Exception $e) { return []; } } From b27605195002ac91b7f8c379b81bc7e7f5d52fdc Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 14:24:24 +0100 Subject: [PATCH 46/93] Inline some variables --- pfsense_zbx.php | 136 +++++++++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 77 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 74c1f27..440c4c8 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -27,9 +27,7 @@ require_once("system.inc"); require_once("util.inc"); define("COMMAND_HANDLERS", build_method_lookup(Commands::class)); - define("DISCOVERY_SECTION_HANDLERS", build_method_lookup(Discoveries::class)); - define("SERVICES_VALUES", build_method_lookup(Services::class)); define("TEXT_ACTIVE", gettext("active")); @@ -40,8 +38,8 @@ define("TEXT_OFFLINE", gettext("offline")); define("TEXT_ONLINE", gettext("online")); define("TEXT_RESERVED", gettext("reserved")); -const SPEEDTEST_INTERVAL_HOURS = 8; -const SPEEDTEST_INTERVAL_SECONDS = SPEEDTEST_INTERVAL_HOURS * 3600; +const SPEED_TEST_INTERVAL_HOURS = 8; +const SPEED_TEST_INTERVAL_SECONDS = SPEED_TEST_INTERVAL_HOURS * 3600; const VALUE_MAPPINGS = [ "openvpn.server.status" => [ @@ -134,17 +132,16 @@ class Services return Util::b2int(PfEnv::is_service_enabled($short_name)); } - public static function name($service, string $name) + public static function name($service, string $name): string { - echo $name; + return $name; } public static function status(string $service): int { $status = PfEnv::get_service_status($service); - return ($status == "") ? 0 : $status; - + return empty($status) ? 0 : $status; } public static function run_on_carp_slave($service, $name, $short_name, $carpcfr, $stopped_on_carp_slave): int @@ -367,9 +364,9 @@ class Discoveries { public static function gw() { - $gws = PfEnv::return_gateways_status(true); - - self::print_json(array_map(fn($gw) => ["{#GATEWAY}" => $gw["name"]], $gws)); + self::print_json(array_map( + fn($gw) => ["{#GATEWAY}" => $gw["name"]], + PfEnv::return_gateways_status(true))); } public static function wan() @@ -405,21 +402,17 @@ class Discoveries public static function openvpn_server() { - $servers = OpenVpn::get_all_openvpn_servers(); - self::print_json(array_map(fn($server) => [ "{#SERVER}" => $server["vpnid"], "{#NAME}" => self::sanitize_name($server["name"])], - $servers)); + OpenVpn::get_all_servers())); } public static function openvpn_server_user() { - $servers = OpenVpn::get_all_openvpn_servers(); - $servers_with_relevant_mode = array_filter( - $servers, + OpenVpn::get_all_servers(), fn($server) => in_array($server["mode"], ["server_user", "server_tls_user", "server_tls"])); $servers_with_conns = array_filter( @@ -459,29 +452,29 @@ class Discoveries public static function ipsec_ph1() { - $config = PfEnv::cfg(); PfEnv::init_config_arr(array("ipsec", "phase1")); - $a_phase1 = &$config["ipsec"]["phase1"]; + + $config = PfEnv::cfg(); self::print_json(array_map(fn($data) => [ "{#IKEID}" => $data["ikeid"], "{#NAME}" => $data["descr"], - ], $a_phase1)); + ], $config["ipsec"]["phase1"])); } public static function ipsec_ph2() { - $config = PfEnv::cfg(); PfEnv::init_config_arr(array("ipsec", "phase2")); - $a_phase2 = &$config["ipsec"]["phase2"]; + + $config = PfEnv::cfg(); self::print_json(array_map(fn($data) => [ "{#IKEID}" => $data["ikeid"], "{#NAME}" => $data["descr"], "{#UNIQID}" => $data["uniqid"], "{#REQID}" => $data["reqid"], - "{#EXTID}" => $data["ikeid"] . "." . $data["reqid"], - ], $a_phase2)); + "{#EXTID}" => sprintf("%s.%s", $data["ikeid"], $data["reqid"]), + ], $config["ipsec"]["phase2"])); } public static function dhcpfailover() @@ -492,7 +485,6 @@ class Discoveries self::print_json(array_map(fn($data) => [ "{#FAILOVER_GROUP}" => str_replace(" ", "__", $data["name"]), ], $leases["failover"])); - } private static function print_json(array $json) @@ -584,7 +576,7 @@ class Speedtest $is_output_file_older_than_interval = file_exists($output_file_path) && - (time() - filemtime($output_file_path) > SPEEDTEST_INTERVAL_SECONDS); + (time() - filemtime($output_file_path) > SPEED_TEST_INTERVAL_SECONDS); if (!$is_output_file_older_than_interval) { return; } @@ -602,7 +594,7 @@ class Speedtest class OpenVpn { - public static function get_all_openvpn_servers(): array + public static function get_all_servers(): array { $servers = PfEnv::openvpn_get_active_servers(); $sk_servers = PfEnv::openvpn_get_active_servers("p2p"); @@ -660,23 +652,22 @@ class Commands public static function openvpn_servervalue(int $server_id, $value_key) { - $servers = OpenVpn::get_all_openvpn_servers(); - - $maybe_server = Util::array_first($servers, fn($s) => $s["vpnid"] == $server_id); + $maybe_server = Util::array_first(OpenVpn::get_all_servers(), fn($s) => $s["vpnid"] == $server_id); + if (empty($maybe_server)) { + return Util::result(0, true); + } $server_value = self::get_server_value($maybe_server, $value_key); if ($value_key == "conns") { - echo is_array($server_value) ? count($server_value) : 0; - return; + return Util::result(is_array($server_value) ? count($server_value) : 0, true); } if (in_array($value_key, ["status", "mode"])) { - echo self::get_value_mapping("openvpn.server.status", $server_value); - return; + return Util::result(self::get_value_mapping("openvpn.server.status", $server_value), true); } - echo $server_value; + return Util::result($server_value, true); } public static function openvpn_server_uservalue($unique_id, $value_key) @@ -691,15 +682,14 @@ class Commands public static function openvpn_clientvalue($client_id, $value_key, $fallback_value = "none") { - $clients = PfEnv::openvpn_get_active_clients(); - - $client = Util::array_first($clients, fn($client) => $client["vpnid"] == $client_id); - - if (empty($client)) { + $maybe_client = Util::array_first( + PfEnv::openvpn_get_active_clients(), + fn($client) => $client["vpnid"] == $client_id); + if (empty($maybe_client)) { return $fallback_value; } - $maybe_value = $client[$value_key]; + $maybe_value = $maybe_client[$value_key]; $is_known_value_key = array_key_exists($value_key, OPENVPN_CLIENT_VALUE); if ($is_known_value_key) { @@ -709,17 +699,16 @@ class Commands return ($maybe_value == "") ? $fallback_value : $maybe_value; } - public static function service_value($name, $value) + public static function service_value(string $name, string $value) { - $services = PfEnv::get_services(); - $name = str_replace("__", " ", $name); + $sanitized_name = str_replace("__", " ", $name); // List of service which are stopped on CARP Slave. // For now this is the best way I found for filtering out the triggers // Waiting for a way in Zabbix to use Global Regexp in triggers with items discovery $stopped_on_carp_slave = array("haproxy", "radvd", "openvpn.", "openvpn", "avahi"); - $matching_services = array_filter($services, function ($server, $n) { + $matching_services = array_filter(PfEnv::get_services(), function ($server, $n) { foreach (["id", "zone"] as $key) { if (!empty($server[$key])) { return printf("%s.%s", $server["name"], $server[$key]) == $n; @@ -731,7 +720,7 @@ class Commands foreach ($matching_services as $service) { $short_name = $service["name"]; - $carpcfr = $short_name . "."; + $carp_cfr = "$short_name."; $is_known_service_value = array_key_exists($value, SERVICES_VALUES); if (!$is_known_service_value) { @@ -739,7 +728,7 @@ class Commands continue; } - echo SERVICES_VALUES[$value]($service, $name, $short_name, $carpcfr, $stopped_on_carp_slave); + echo SERVICES_VALUES[$value]($service, $sanitized_name, $short_name, $carp_cfr, $stopped_on_carp_slave); } } @@ -756,22 +745,19 @@ class Commands public static function carp_status($echo_result = true): int { - $config = PfEnv::cfg(); - $carp_status = PfEnv::get_carp_status(); - $carp_detected_problems = PfEnv::get_single_sysctl("net.inet.carp.demotion"); - - $is_carp_enabled = $carp_status != 0; - if (!$is_carp_enabled) { // CARP is disabled + $is_carp_enabled = PfEnv::get_carp_status() != 0; + if (!$is_carp_enabled) { return Util::result(CARP_STATUS_DISABLED, $echo_result); } - if ($carp_detected_problems != 0) { - // There's some Major Problems with CARP + $is_carp_demotion_status_ok = PfEnv::get_single_sysctl("net.inet.carp.demotion") == 0; + if (!$is_carp_demotion_status_ok) { return Util::result(CARP_STATUS_PROBLEM, $echo_result); } - $virtual_ips = $config["virtualip"]["vip"]; - $just_carps = array_filter($virtual_ips, fn($virtual_ip) => $virtual_ip["mode"] != "carp"); + $config = PfEnv::cfg(); + + $just_carps = array_filter($config["virtualip"]["vip"], fn($virtual_ip) => $virtual_ip["mode"] != "carp"); $status_str = array_reduce($just_carps, function ($status, $carp) { $if_status = PfEnv::get_carp_interface_status("_vip{$carp["uniqid"]}"); @@ -789,9 +775,9 @@ class Commands $is_known_carp_status = array_key_exists($status_str, CARP_RES); - $result = $is_known_carp_status ? CARP_RES[$status_str] : CARP_STATUS_UNKNOWN; - - return Util::result($result, $echo_result); + return Util::result( + $is_known_carp_status ? CARP_RES[$status_str] : CARP_STATUS_UNKNOWN, + $echo_result); } @@ -799,8 +785,7 @@ class Commands public static function system($section) { if ($section === "packages_update") { - echo self::get_outdated_packages(); - return; + return Util::result(self::get_outdated_packages(), true); } $system_pkg_version = PfEnv::get_system_pkg_version(); @@ -808,13 +793,14 @@ class Commands $installed_version = $system_pkg_version["installed_version"]; if ($section === "new_version_available") { - echo Util::b2int($version != $installed_version); - return; + return Util::result(Util::b2int($version != $installed_version), true); } if (array_key_exists($section, $system_pkg_version)) { - echo $system_pkg_version[$section]; + return Util::result($system_pkg_version[$section], true); } + + return Util::result("version", true); } public static function ipsec_ph1($ike_id, $value_key) @@ -945,7 +931,9 @@ class Commands { $line = "-------------------\n"; - $ovpn_servers = OpenVpn::get_all_openvpn_servers(); + $config = PfEnv::cfg(); + + $ovpn_servers = OpenVpn::get_all_servers(); echo "OPENVPN Servers:\n"; print_r($ovpn_servers); echo $line; @@ -973,8 +961,6 @@ class Commands echo $line; echo "IPsec: \n"; - - $config = PfEnv::cfg(); PfEnv::init_config_arr(array("ipsec", "phase1")); PfEnv::init_config_arr(array("ipsec", "phase2")); $status = PfEnv::ipsec_list_sa(); @@ -992,7 +978,6 @@ class Commands echo $line; - //Packages echo "Packages: \n"; $installed_packages = PfEnv::get_pkg_info("all", false, true); print_r($installed_packages); @@ -1002,7 +987,7 @@ class Commands { list($server_id, $user_id) = explode("+", $unique_id); - $servers = OpenVpn::get_all_openvpn_servers(); + $servers = OpenVpn::get_all_servers(); $maybe_server = Util::array_first($servers, fn($server) => $server["vpnid"] == $server_id); if (!$maybe_server) { @@ -1277,11 +1262,8 @@ class Commands private static function get_outdated_packages(): int { - $installed_packages = PfEnv::get_pkg_info("all", false, true); - - return count(array_filter( - $installed_packages, + PfEnv::get_pkg_info("all", false, true), fn($p) => $p["version"] != $p["installed_version"])); } @@ -1299,10 +1281,10 @@ class Commands return $default_value; } - $value = strtolower($value); - $is_value_with_known_mapping = array_key_exists($value, $value_mapping); + $sanitized_value = strtolower($value); + $is_value_with_known_mapping = array_key_exists($sanitized_value, $value_mapping); - return $is_value_with_known_mapping ? $value_mapping[$value] : $default_value; + return $is_value_with_known_mapping ? $value_mapping[$sanitized_value] : $default_value; } } From 20b631cf9b27373d5713862f5df8e939aabecff2 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 14:32:42 +0100 Subject: [PATCH 47/93] Inline some variables and simplify code --- pfsense_zbx.php | 55 +++++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 440c4c8..4a04500 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -405,14 +405,14 @@ class Discoveries self::print_json(array_map(fn($server) => [ "{#SERVER}" => $server["vpnid"], "{#NAME}" => self::sanitize_name($server["name"])], - OpenVpn::get_all_servers())); + OpenVpn::get_active_servers())); } public static function openvpn_server_user() { $servers_with_relevant_mode = array_filter( - OpenVpn::get_all_servers(), + OpenVpn::get_active_servers(), fn($server) => in_array($server["mode"], ["server_user", "server_tls_user", "server_tls"])); $servers_with_conns = array_filter( @@ -594,7 +594,7 @@ class Speedtest class OpenVpn { - public static function get_all_servers(): array + public static function get_active_servers(): array { $servers = PfEnv::openvpn_get_active_servers(); $sk_servers = PfEnv::openvpn_get_active_servers("p2p"); @@ -652,7 +652,7 @@ class Commands public static function openvpn_servervalue(int $server_id, $value_key) { - $maybe_server = Util::array_first(OpenVpn::get_all_servers(), fn($s) => $s["vpnid"] == $server_id); + $maybe_server = Util::array_first(OpenVpn::get_active_servers(), fn($s) => $s["vpnid"] == $server_id); if (empty($maybe_server)) { return Util::result(0, true); } @@ -807,32 +807,28 @@ class Commands { // Get Value from IPsec Phase 1 Configuration // If Getting "disabled" value only check item presence in config array - $config = PfEnv::cfg(); PfEnv::init_config_arr(["ipsec", "phase1"]); - $a_phase1 = &$config["ipsec"]["phase1"]; + + $config = PfEnv::cfg(); if ($value_key == "status") { - echo Commands::get_ipsec_status($ike_id); - return; + return Util::result(Commands::get_ipsec_status($ike_id), true); } if ($value_key == "disabled") { - echo "0"; - return; + return Util::result("0", true); } - $maybe_ike_match = Util::array_first($a_phase1, fn($d) => $d["ikeid"] == $ike_id); + $maybe_ike_match = Util::array_first($config["ipsec"]["phase1"], fn($d) => $d["ikeid"] == $ike_id); if (empty($maybe_ike_match)) { - echo ""; - return; + return Util::result("", true); } if (!array_key_exists($value_key, $maybe_ike_match)) { - echo ""; - return; + return Util::result("", true); } - echo self::get_value_mapping("ipsec." . $value_key, $maybe_ike_match[$value_key]); + return Util::result(self::get_value_mapping("ipsec.$value_key", $maybe_ike_match[$value_key])); } public static function ipsec_ph2($uniqid, $value_key) @@ -933,21 +929,18 @@ class Commands $config = PfEnv::cfg(); - $ovpn_servers = OpenVpn::get_all_servers(); echo "OPENVPN Servers:\n"; - print_r($ovpn_servers); + print_r(OpenVpn::get_active_servers()); echo $line; - $ovpn_clients = PfEnv::openvpn_get_active_clients(); echo "OPENVPN Clients:\n"; - print_r($ovpn_clients); + print_r(PfEnv::openvpn_get_active_clients()); echo $line; $ifdescrs = PfEnv::get_configured_interface_with_descr(true); $ifaces = []; foreach ($ifdescrs as $ifdescr => $ifname) { - $ifinfo = PfEnv::get_interface_info($ifdescr); - $ifaces[$ifname] = $ifinfo; + $ifaces[$ifname] = PfEnv::get_interface_info($ifdescr); } echo "Network Interfaces:\n"; print_r($ifaces); @@ -955,39 +948,33 @@ class Commands print_r(PfEnv::get_configured_interface_list()); echo $line; - $services = PfEnv::get_services(); echo "Services: \n"; - print_r($services); + print_r(PfEnv::get_services()); echo $line; echo "IPsec: \n"; PfEnv::init_config_arr(array("ipsec", "phase1")); PfEnv::init_config_arr(array("ipsec", "phase2")); - $status = PfEnv::ipsec_list_sa(); echo "IPsec Status: \n"; - print_r($status); - - $a_phase1 = &$config["ipsec"]["phase1"]; - $a_phase2 = &$config["ipsec"]["phase2"]; + print_r(PfEnv::ipsec_list_sa()); echo "IPsec Config Phase 1: \n"; - print_r($a_phase1); + print_r($config["ipsec"]["phase1"]); echo "IPsec Config Phase 2: \n"; - print_r($a_phase2); + print_r($config["ipsec"]["phase2"]); echo $line; echo "Packages: \n"; - $installed_packages = PfEnv::get_pkg_info("all", false, true); - print_r($installed_packages); + print_r(PfEnv::get_pkg_info("all", false, true)); } private static function get_openvpn_server_uservalue_($unique_id, $value_key, $default = "") { list($server_id, $user_id) = explode("+", $unique_id); - $servers = OpenVpn::get_all_servers(); + $servers = OpenVpn::get_active_servers(); $maybe_server = Util::array_first($servers, fn($server) => $server["vpnid"] == $server_id); if (!$maybe_server) { From f2bdbd222ccb3e949990c912e4e148282323678f Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 14:55:49 +0100 Subject: [PATCH 48/93] Rename Speedtest class --- pfsense_zbx.php | 59 +++++++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 4a04500..60266c7 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -328,6 +328,16 @@ class Util return $result; } + + public static function space_to_underscore($value) + { + return str_replace(" ", "__", $value); + } + + public static function underscore_to_space($value) + { + return str_replace("__", " ", $value); + } } class Interfaces @@ -439,7 +449,7 @@ class Discoveries $id = is_null($maybe_id) ? "" : $service[$maybe_id]; return [ - "{#SERVICE}" => str_replace(" ", "__", $service["name"]) . $id, + "{#SERVICE}" => sprintf("%s%s", Util::space_to_underscore($service["name"]), $id), "{#DESCRIPTION}" => $service["description"], ]; }, $named_services)); @@ -483,7 +493,7 @@ class Discoveries $leases = PfEnv::system_get_dhcpleases(); self::print_json(array_map(fn($data) => [ - "{#FAILOVER_GROUP}" => str_replace(" ", "__", $data["name"]), + "{#FAILOVER_GROUP}" => Util::space_to_underscore($data["name"]), ], $leases["failover"])); } @@ -540,7 +550,7 @@ class Discoveries } } -class Speedtest +class SpeedTest { public static function interface_value($if_name, $value) { @@ -551,18 +561,22 @@ class Speedtest return; } - $speedtest_data = json_decode(file_get_contents($filename), true); - if (array_key_exists($value, $speedtest_data)) { + $speed_test_data = json_decode(file_get_contents($filename), true); + if (array_key_exists($value, $speed_test_data)) { return; } - echo empty($tv1) ? $speedtest_data[$value] : $speedtest_data[$tv0][$tv1]; + echo empty($tv1) ? $speed_test_data[$value] : $speed_test_data[$tv0][$tv1]; } public static function cron_install($enable = true) { - $command = "/usr/local/bin/php " . __FILE__ . " speedtest_cron"; - PfEnv::install_cron_job($command, $enable, "*/15", "*", "*", "*", "*", "root", true); + PfEnv::install_cron_job( + implode(" ", ["/usr/local/bin/php", __FILE__, "speedtest_cron"]), + $enable, + "*/15", "*", "*", "*", "*", + "root", + true); } public static function exec($if_name, $ip_address) @@ -619,21 +633,22 @@ class Commands { $gws = PfEnv::return_gateways_status(true); - $is_known_gw = array_key_exists($gw, $gws); - if (!$is_known_gw) { - return; + $maybe_gw = array_key_exists($gw, $gws) ? $gws[$gw] : null; + if (!$maybe_gw) { + return Util::result(""); } - $value = $gws[$gw][$value_key]; + $value = $maybe_gw[$value_key]; if ($value_key != "status") { - echo $value; - return; + return Util::result($value); } - // Issue #70: Gateway Forced Down - $v = ($gws[$gw]["substatus"] != "none") ? $gws[$gw]["substatus"] : $value; + $substatus = $maybe_gw["substatus"]; + $has_relevant_substatus = $substatus != "none"; // Issue #70: Gateway Forced Down - echo self::get_value_mapping("gateway.status", $v); + return Util::result(self::get_value_mapping( + "gateway.status", + $has_relevant_substatus ? $substatus : $value)); } public static function gw_status() @@ -646,8 +661,8 @@ class Commands public static function if_speedtest_value($if_name, $value) { - Speedtest::cron_install(); - Speedtest::interface_value($if_name, $value); + SpeedTest::cron_install(); + SpeedTest::interface_value($if_name, $value); } public static function openvpn_servervalue(int $server_id, $value_key) @@ -701,7 +716,7 @@ class Commands public static function service_value(string $name, string $value) { - $sanitized_name = str_replace("__", " ", $name); + $sanitized_name = Util::underscore_to_space($name); // List of service which are stopped on CARP Slave. // For now this is the best way I found for filtering out the triggers @@ -876,13 +891,13 @@ class Commands public static function speedtest_cron() { foreach (Interfaces::retrieve_wan_interfaces() as $if_info) { - Speedtest::exec($if_info["hwif"], $if_info["ipaddr"]); + SpeedTest::exec($if_info["hwif"], $if_info["ipaddr"]); } } public static function cron_cleanup() { - Speedtest::cron_install(false); + SpeedTest::cron_install(false); } // S.M.A.R.T Status From 283ffb6ffebcc103286272218414ca9ada12f1d3 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 15:02:24 +0100 Subject: [PATCH 49/93] Sanitize VALUE_MAPPING dictionary --- pfsense_zbx.php | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 60266c7..dbadc90 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -43,31 +43,31 @@ const SPEED_TEST_INTERVAL_SECONDS = SPEED_TEST_INTERVAL_HOURS * 3600; const VALUE_MAPPINGS = [ "openvpn.server.status" => [ - "down" => "0", - "up" => "1", - "none" => "2", - "reconnecting; ping-restart" => "3", - "waiting" => "4", - "server_user_listening" => "5"], + "down" => 0, + "up" => 1, + "none" => 2, + "reconnecting; ping-restart" => 3, + "waiting" => 4, + "server_user_listening" => 5], "openvpn.client.status" => [ - "up" => "1", - "down" => "0", - "none" => "0", - "reconnecting; ping-restart" => "2"], + "up" => 1, + "down" => 0, + "none" => 0, + "reconnecting; ping-restart" => 2], "openvpn.server.mode" => [ - "p2p_tls" => "1", - "p2p_shared_key" => "2", - "server_tls" => "3", - "server_user" => "4", - "server_tls_user" => "5"], + "p2p_tls" => 1, + "p2p_shared_key" => 2, + "server_tls" => 3, + "server_user" => 4, + "server_tls_user" => 5], "gateway.status" => [ - "online" => "0", - "none" => "0", - "loss" => "1", - "highdelay" => "2", - "highloss" => "3", - "force_down" => "4", - "down" => "5"], + "online" => 0, + "none" => 0, + "loss" => 1, + "highdelay" => 2, + "highloss" => 3, + "force_down" => 4, + "down" => 5], "ipsec.iketype" => [ "auto" => 0, "ikev1" => 1, From c3695d6345750e80922868a55b9f00fae32dc91b Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 15:09:28 +0100 Subject: [PATCH 50/93] Simplify and fix Command::system --- pfsense_zbx.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index dbadc90..5f43bf4 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -804,18 +804,15 @@ class Commands } $system_pkg_version = PfEnv::get_system_pkg_version(); - $version = $system_pkg_version["version"]; - $installed_version = $system_pkg_version["installed_version"]; - if ($section === "new_version_available") { - return Util::result(Util::b2int($version != $installed_version), true); + return Util::result( + Util::b2int($system_pkg_version["version"] != $system_pkg_version["installed_version"]), + true); } - if (array_key_exists($section, $system_pkg_version)) { - return Util::result($system_pkg_version[$section], true); - } + $is_known_section = array_key_exists($section, $system_pkg_version); - return Util::result("version", true); + return Util::result($is_known_section ? $system_pkg_version[$section] : "", true); } public static function ipsec_ph1($ike_id, $value_key) From 6c9eb547d753bd68985821ef6b8324afe4123ad0 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 19:57:43 +0100 Subject: [PATCH 51/93] Refactor and fix get_dhcp --- pfsense_zbx.php | 316 ++++++++++++++++++++++++++++-------------------- 1 file changed, 184 insertions(+), 132 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 5f43bf4..dc052bf 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -162,11 +162,11 @@ class PfEnv return $config; } - public static function g() + public static function g($key) { global $g; - return $g; + return $g[$key]; } private static function call_pfsense_method_with_same_name_and_arguments() @@ -619,6 +619,20 @@ class OpenVpn class Commands { + private const BINDING_STATES = [ + "active" => [ + "act" => TEXT_ACTIVE, + ], + "free" => [ + "act" => TEXT_EXPIRED, + "online" => TEXT_OFFLINE, + ], + "backup" => [ + "act" => TEXT_RESERVED, + "online" => TEXT_OFFLINE, + ], + ]; + public static function discovery($section) { $is_known_section = in_array(strtolower($section), DISCOVERY_SECTION_HANDLERS); @@ -1105,148 +1119,186 @@ class Commands ), [])); } - // Get DHCP Arrays (copied from status_dhcp_leases.php, waiting for pfsense 2.5, in order to use system_get_dhcpleases();) - private static function get_dhcp($value_key) + private static function parse_raw_record(string $raw_lease_data): array { - $g = PfEnv::g(); + $lease_data_lines = + array_filter(array_map(fn($m) => trim($m), explode(";", $raw_lease_data))); - $leases_file = "{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases"; + return array_reduce( + $lease_data_lines, + function ($p, $lease_data_line) { + list($k, $v) = array_pad(explode(" ", $lease_data_line, 2), 2, true); + return array_merge($p, [$k => $v]); + }, + []); + } + + private static function parse_failover_record(array $data): array + { + list($name, $raw_lease_data) = array_map(fn($m) => trim($m), $data); + + return [ + "type" => "failover", + "data" => array_merge( + ["name" => $name], + self::parse_raw_record($raw_lease_data))]; + } + + private static function parse_lease_record(array $data): array + { + list($lease_address, $raw_lease_data) = array_map(fn($m) => trim($m), $data); + + return [ + "type" => "lease", + "data" => array_merge( + ["ip" => $lease_address], + self::parse_raw_record($raw_lease_data))]; + } + + private static function parse_dhcp_record(string $record): ?array + { + $is_lease_record = preg_match("/^lease\s+(.*)\s+\{(.+)\}$/", $record, $lease_record_match); + $is_failover_record = preg_match("/^failover.*\"(.*)\"\s+state\s+\{(.+)\}$/", $record, $failover_record_match); + + $is_known_record_type = $is_lease_record || $is_failover_record; + if (!$is_known_record_type) { + return null; + } + + if ($is_lease_record) { + return self::parse_lease_record(array_slice($lease_record_match, 1)); + } + + return self::parse_failover_record(array_slice($failover_record_match, 1)); + } + + private static function read_dhcp_records_from_file(string $leases_file): array + { $awk = "/usr/bin/awk"; - /* this pattern sticks comments into a single array item */ - $clean_pattern = "'{ gsub(\"#.*\", \"\");} { gsub(\";\", \"\"); print;}'"; - /* We then split the leases file by } */ - $split_pattern = "'BEGIN { RS=\"}\";} {for (i=1; i<=NF; i++) printf \"%s \", \$i; printf \"}\\n\";}'"; - /* stuff the leases file in a proper format into an array by line */ - @exec("/bin/cat $leases_file 2>/dev/null| $awk $clean_pattern | $awk $split_pattern", $leases_content); - $leases_count = count($leases_content); - @exec("/usr/sbin/arp -an", $rawdata); + // Remove all content up to the first lease record + $clean_pattern = "'/lease.*{\$/,0'"; + + // Split file into records by '}' + $split_pattern = "'BEGIN { RS=ORS=\"}\" } { gsub(\"\\n\", \"\"); print; printf \"\\n\"}'"; + + // Stuff the leases file in a proper format into an array by line + exec( + "/bin/cat $leases_file 2>/dev/null | $awk $clean_pattern | $awk $split_pattern", + $raw_lease_records); + + $relevant_records = array_filter($raw_lease_records, fn($r) => preg_match("/^lease.*|^failover.*/", $r)); + + return array_map(fn($r) => self::parse_dhcp_record($r), $relevant_records); + } + + private static function binding_to_state($binding): array + { + $is_known_binding = array_key_exists($binding, BINDING_STATES); + if (!$is_known_binding) { + return [ + "act" => "", + ]; + } + + return BINDING_STATES[$binding]; + } + + private static function raw_lease_record_to_lease(array $raw_lease_record, array $arpdata_ip): array + { + $data = $raw_lease_record["data"]; + + $ip = $data["ip"]; + $maybe_client_hostname = + array_key_exists("client-hostname", $data) ? + str_replace("\"", "", $data["client-hostname"]) : + null; + + list(, $binding) = explode(" ", $data["binding"]); + list(, $mac) = explode(" ", $data["hardware"]); + list(, $start_date, $start_time) = explode(" ", $data["starts"]); + + $hostname = + !empty($maybe_client_hostname) ? + preg_replace('/"/', "", $maybe_client_hostname) : + gethostbyaddr($data["ip"]); + + $online = in_array($data["ip"], $arpdata_ip) ? TEXT_ONLINE : TEXT_OFFLINE; + + $binding_state = self::binding_to_state($binding); + + $start = implode(" ", [$start_date, $start_time]); + list(, $end_date, $end_time) = array_pad(explode(" ", $data["ends"]), 3, null); + + $end = ($end_date == "never") ? TEXT_NEVER : implode(" ", [$end_date, $end_time]); + + return array_merge(compact("end", "hostname", "ip", "mac", "online", "start"), $binding_state); + } + + private static function raw_failover_record_to_pool(array $raw_failover_record): array + { + $data = $raw_failover_record["data"]; + + $n0 = $data["name"]; + + $friendly_description = $n0; // PfEnv::convert_friendly_interface_to_friendly_descr(substr($n0, 5)); + $name = "$n0 ($friendly_description)"; + + list($my_state_str, $my_time_str) = explode(" at ", $data["my"]); + list($partner_state_str, $partner_time_str) = explode(" at ", $data["partner"]); + + $my_state_parts = explode(" ", $my_state_str); + $partner_state_parts = explode(" ", $partner_state_str); + $my_time_parts = explode(" ", $my_time_str); + $partner_time_parts = explode(" ", $partner_time_str); + + $my_state = $my_state_parts[1]; + $partner_state = $partner_state_parts[1]; + $my_time = implode(" ", array_slice($my_time_parts, 1)); + $partner_time = implode(" ", array_slice($partner_time_parts, 1)); + + return [ + "name" => $name, + "mystate" => $my_state, + "peerstate" => $partner_state, + "mydate" => $my_time, + "peerdate" => $partner_time, + ]; + } + + private static function arp_ips() + { + exec("/usr/sbin/arp -an | awk '{ gsub(/[()]/,\"\") } {print $2}'", $arp_data); + + return $arp_data; + } + + // Get DHCP Arrays (copied from status_dhcp_leases.php, waiting for pfsense 2.5, in order to use system_get_dhcpleases();) + private static function get_dhcp($value_key): array + { + $leases_file = implode( + DIRECTORY_SEPARATOR, + [PfEnv::g("dhcpd_chroot_path"), "var", "db", "dhcpd.leases"]); + + $dhcp_records = self::read_dhcp_records_from_file($leases_file); $failover = []; - $leases = []; - $pools = []; - foreach ($leases_content as $lease) { - /* split the line by space */ - $data = explode(" ", $lease); - /* walk the fields */ - $f = 0; - $fcount = count($data); - /* with less than 20 fields there is nothing useful */ - if ($fcount < 20) { - $i++; - continue; - } - while ($f < $fcount) { - switch ($data[$f]) { - case "failover": - $pools[$p]['name'] = trim($data[$f + 2], '"'); - $pools[$p]["name"] = "{$pools[$p]["name"]} (" . PfEnv::convert_friendly_interface_to_friendly_descr(substr($pools[$p]["name"], 5)) . ")"; - $pools[$p]["mystate"] = $data[$f + 7]; - $pools[$p]["peerstate"] = $data[$f + 14]; - $pools[$p]["mydate"] = $data[$f + 10]; - $pools[$p]["mydate"] .= " " . $data[$f + 11]; - $pools[$p]["peerdate"] = $data[$f + 17]; - $pools[$p]["peerdate"] .= " " . $data[$f + 18]; - $p++; - $i++; - continue 3; - case "lease": - $leases[$l]["ip"] = $data[$f + 1]; - $leases[$l]["type"] = TEXT_DYNAMIC; - $f = $f + 2; - break; - case "starts": - $leases[$l]["start"] = $data[$f + 2]; - $leases[$l]["start"] .= " " . $data[$f + 3]; - $f = $f + 3; - break; - case "ends": - if ($data[$f + 1] == "never") { - // Quote from dhcpd.leases(5) man page: - // If a lease will never expire, date is never instead of an actual date. - $leases[$l]["end"] = TEXT_NEVER; - $f = $f + 1; - } else { - $leases[$l]["end"] = $data[$f + 2]; - $leases[$l]["end"] .= " " . $data[$f + 3]; - $f = $f + 3; - } - break; - case "tsfp": - case "atsfp": - case "cltt": - case "tstp": - $f = $f + 3; - break; - case "binding": - switch ($data[$f + 2]) { - case "active": - $leases[$l]["act"] = TEXT_ACTIVE; - break; - case "free": - $leases[$l]["act"] = TEXT_EXPIRED; - $leases[$l]["online"] = TEXT_OFFLINE; - break; - case "backup": - $leases[$l]["act"] = TEXT_RESERVED; - $leases[$l]["online"] = TEXT_OFFLINE; - break; - } - $f = $f + 1; - break; - case "next": - /* skip the next binding statement */ - $f = $f + 3; - break; - case "rewind": - /* skip the rewind binding statement */ - $f = $f + 3; - break; - case "hardware": - $leases[$l]["mac"] = $data[$f + 2]; - $arpdata_ip = []; - /* check if it's online and the lease is active */ - $leases[$l]["online"] = - (in_array($leases[$l]["ip"], $arpdata_ip)) ? TEXT_ONLINE : TEXT_OFFLINE; - $f = $f + 2; - break; - case "client-hostname": - if ($data[$f + 1] <> "") { - $leases[$l]["hostname"] = preg_replace('/"/', "", $data[$f + 1]); - } else { - $hostname = gethostbyaddr($leases[$l]["ip"]); - if ($hostname <> "") { - $leases[$l]["hostname"] = $hostname; - } - } - $f = $f + 1; - break; - case "uid": - $f = $f + 1; - break; - } - $f++; - } - $l++; - $i++; - /* slowly chisel away at the source array */ - array_shift($leases_content); - } - /* remove duplicate items by mac address */ - if (count($leases) > 0) { - $leases = self::remove_duplicates($leases, "ip"); + if ($value_key === "failover") { + return $failover; } - if (count($pools) > 0) { - $pools = self::remove_duplicates($pools, "name"); - asort($pools); + if ($value_key === "pools") { + $failover_records = array_filter($dhcp_records, fn($r) => $r["type"] == "failover"); + + return self::remove_duplicates(array_map(fn($r) => self::raw_failover_record_to_pool($r), $failover_records), "name"); } - $rs = compact("pools", "failover", "leases"); - $is_known_value_key = array_key_exists($value_key, $rs); + $lease_records = array_filter($dhcp_records, fn($r) => $r["type"] == "lease"); - return $is_known_value_key ? $rs[$value_key] : $leases; + $arp_ips = self::arp_ips(); + + return self::remove_duplicates(array_map(fn($r) => self::raw_lease_record_to_lease($r, $arp_ips), $lease_records), "mac"); } private static function check_dhcp_failover(): int From d66c98cb419b8a77245a41fbd40d5ef15eba756e Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 20:04:10 +0100 Subject: [PATCH 52/93] Fix style issues --- pfsense_zbx.php | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index dc052bf..e39c7a6 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1242,29 +1242,21 @@ class Commands $n0 = $data["name"]; - $friendly_description = $n0; // PfEnv::convert_friendly_interface_to_friendly_descr(substr($n0, 5)); + $friendly_description = PfEnv::convert_friendly_interface_to_friendly_descr(substr($n0, 5)); $name = "$n0 ($friendly_description)"; list($my_state_str, $my_time_str) = explode(" at ", $data["my"]); list($partner_state_str, $partner_time_str) = explode(" at ", $data["partner"]); - $my_state_parts = explode(" ", $my_state_str); - $partner_state_parts = explode(" ", $partner_state_str); - $my_time_parts = explode(" ", $my_time_str); - $partner_time_parts = explode(" ", $partner_time_str); + list(, $mystate) = explode(" ", $my_state_str); + list(, $peerstate) = explode(" ", $partner_state_str); + list(, $my_date, $my_time) = explode(" ", $my_time_str); + list(, $partner_date, $partner_time) = explode(" ", $partner_time_str); - $my_state = $my_state_parts[1]; - $partner_state = $partner_state_parts[1]; - $my_time = implode(" ", array_slice($my_time_parts, 1)); - $partner_time = implode(" ", array_slice($partner_time_parts, 1)); + $mydate = implode(" ", [$my_date, $my_time]); + $peerdate = implode(" ", [$partner_date, $partner_time]); - return [ - "name" => $name, - "mystate" => $my_state, - "peerstate" => $partner_state, - "mydate" => $my_time, - "peerdate" => $partner_time, - ]; + return compact("name", "mystate", "peerstate", "mydate", "peerdate"); } private static function arp_ips() From 1774d6ade9e86d34afde927167cc7c37f796bc6c Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 20:08:37 +0100 Subject: [PATCH 53/93] Fix invalid const reference --- pfsense_zbx.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index e39c7a6..f689721 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1195,14 +1195,14 @@ class Commands private static function binding_to_state($binding): array { - $is_known_binding = array_key_exists($binding, BINDING_STATES); + $is_known_binding = array_key_exists($binding, self::BINDING_STATES); if (!$is_known_binding) { return [ "act" => "", ]; } - return BINDING_STATES[$binding]; + return self::BINDING_STATES[$binding]; } private static function raw_lease_record_to_lease(array $raw_lease_record, array $arpdata_ip): array From 5a4abe940c699e4b68c4f8f5d1424c8e8acaacc0 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 20:20:34 +0100 Subject: [PATCH 54/93] Add offline leases check --- pfsense_zbx.php | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index f689721..4752319 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -886,11 +886,11 @@ class Commands public static function dhcp($section) { - if ($section != "failover") { - return; + if ($section === "failover") { + return Util::result(self::check_dhcp_failover(), true); } - echo self::check_dhcp_failover(); + return Util::result(self::check_dhcp_offline_leases(), true); } // File is present @@ -1293,14 +1293,23 @@ class Commands return self::remove_duplicates(array_map(fn($r) => self::raw_lease_record_to_lease($r, $arp_ips), $lease_records), "mac"); } + private static function check_dhcp_offline_leases(): int + { + return count(array_filter( + self::get_dhcp("leases"), + fn($f) => $f["online"] != TEXT_ONLINE)); + } + private static function check_dhcp_failover(): int { // Check DHCP Failover Status // Returns number of failover pools which state is not normal or // different from peer state - $failover = self::get_dhcp("failover"); + $failover_pools = self::get_dhcp("pools"); - return count(array_filter($failover, fn($f) => ($f["mystate"] != "normal") || ($f["mystate"] != $f["peerstate"]))); + return count(array_filter( + $failover_pools, + fn($f) => ($f["mystate"] != "normal") || ($f["mystate"] != $f["peerstate"]))); } private static function get_outdated_packages(): int From 4afae8cba51d51a59c9818876d9d4ed1dfc6dc67 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 21:15:51 +0100 Subject: [PATCH 55/93] Fix service_value --- pfsense_zbx.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 4752319..f1dfe1b 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -737,10 +737,10 @@ class Commands // Waiting for a way in Zabbix to use Global Regexp in triggers with items discovery $stopped_on_carp_slave = array("haproxy", "radvd", "openvpn.", "openvpn", "avahi"); - $matching_services = array_filter(PfEnv::get_services(), function ($server, $n) { + $matching_services = array_filter(PfEnv::get_services(), function ($server) use ($sanitized_name) { foreach (["id", "zone"] as $key) { if (!empty($server[$key])) { - return printf("%s.%s", $server["name"], $server[$key]) == $n; + return printf("%s.%s", $server["name"], $server[$key]) == $sanitized_name; } } @@ -1299,7 +1299,7 @@ class Commands self::get_dhcp("leases"), fn($f) => $f["online"] != TEXT_ONLINE)); } - + private static function check_dhcp_failover(): int { // Check DHCP Failover Status From 3ff4bb44b7d74a70e104c5779e7440092e91b201 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 21:43:58 +0100 Subject: [PATCH 56/93] Fix services_value --- pfsense_zbx.php | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index f1dfe1b..fcc2377 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -28,7 +28,7 @@ require_once("util.inc"); define("COMMAND_HANDLERS", build_method_lookup(Commands::class)); define("DISCOVERY_SECTION_HANDLERS", build_method_lookup(Discoveries::class)); -define("SERVICES_VALUES", build_method_lookup(Services::class)); +define("SERVICES_VALUE_ACTIONS", build_method_lookup(Services::class)); define("TEXT_ACTIVE", gettext("active")); define("TEXT_DYNAMIC", gettext("dynamic")); @@ -715,17 +715,17 @@ class Commands PfEnv::openvpn_get_active_clients(), fn($client) => $client["vpnid"] == $client_id); if (empty($maybe_client)) { - return $fallback_value; + return Util::result($fallback_value, true); } $maybe_value = $maybe_client[$value_key]; $is_known_value_key = array_key_exists($value_key, OPENVPN_CLIENT_VALUE); if ($is_known_value_key) { - return OPENVPN_CLIENT_VALUE[$value_key]($maybe_value); + return Util::result(OPENVPN_CLIENT_VALUE[$value_key]($maybe_value), true); } - return ($maybe_value == "") ? $fallback_value : $maybe_value; + return Util::result(empty($maybe_value) ? $fallback_value : $maybe_value); } public static function service_value(string $name, string $value) @@ -737,28 +737,36 @@ class Commands // Waiting for a way in Zabbix to use Global Regexp in triggers with items discovery $stopped_on_carp_slave = array("haproxy", "radvd", "openvpn.", "openvpn", "avahi"); - $matching_services = array_filter(PfEnv::get_services(), function ($server) use ($sanitized_name) { + $maybe_service = Util::array_first(PfEnv::get_services(), function ($service) use ($sanitized_name) { foreach (["id", "zone"] as $key) { - if (!empty($server[$key])) { - return printf("%s.%s", $server["name"], $server[$key]) == $sanitized_name; + if (!empty($service[$key])) { + return sprintf("%s.%s", $service["name"], $service[$key]) == $sanitized_name; } } - return false; + return $service["name"] == $sanitized_name; }); - foreach ($matching_services as $service) { - $short_name = $service["name"]; - $carp_cfr = "$short_name."; - - $is_known_service_value = array_key_exists($value, SERVICES_VALUES); - if (!$is_known_service_value) { - echo $service[$value]; - continue; - } - - echo SERVICES_VALUES[$value]($service, $sanitized_name, $short_name, $carp_cfr, $stopped_on_carp_slave); + if (empty($maybe_service)) { + return Util::result("", true); } + + $short_name = $maybe_service["name"]; + $carp_cfr = "$short_name."; + + $is_known_service_value = array_key_exists($value, SERVICES_VALUE_ACTIONS); + if (!$is_known_service_value) { + return Util::result($maybe_service[$value], true); + } + + return Util::result( + SERVICES_VALUE_ACTIONS[$value]( + $maybe_service, + $sanitized_name, + $short_name, + $carp_cfr, + $stopped_on_carp_slave), + true); } public static function temperature($sensorid) From 835816aab0e8b698418b9fb546c847b253c18703 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 21:45:42 +0100 Subject: [PATCH 57/93] Fix Util::result --- pfsense_zbx.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index fcc2377..8fdb73b 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -323,7 +323,7 @@ class Util public static function result($result, bool $echo_result = false) { if ($echo_result) { - echo $echo_result; + echo $result; } return $result; From 31a0b0ad19168c006227e6da4559c71e0b6f3261 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 21:50:41 +0100 Subject: [PATCH 58/93] Output result for OpenVPN client value --- pfsense_zbx.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 8fdb73b..9bde590 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -725,7 +725,7 @@ class Commands return Util::result(OPENVPN_CLIENT_VALUE[$value_key]($maybe_value), true); } - return Util::result(empty($maybe_value) ? $fallback_value : $maybe_value); + return Util::result(empty($maybe_value) ? $fallback_value : $maybe_value, true); } public static function service_value(string $name, string $value) From 6e7b1bb56b8433232ba3afb431415c4ed5b4ab06 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 22:00:05 +0100 Subject: [PATCH 59/93] Fix service identifier concatenation --- pfsense_zbx.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 9bde590..e5ddbb5 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -740,7 +740,7 @@ class Commands $maybe_service = Util::array_first(PfEnv::get_services(), function ($service) use ($sanitized_name) { foreach (["id", "zone"] as $key) { if (!empty($service[$key])) { - return sprintf("%s.%s", $service["name"], $service[$key]) == $sanitized_name; + return sprintf("%s%s", $service["name"], $service[$key]) == $sanitized_name; } } From e47eb9f53a871aa247f76bb9c9720ba792a5bfe9 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 22:14:25 +0100 Subject: [PATCH 60/93] Fix service identification --- pfsense_zbx.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index e5ddbb5..5fc97d6 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -739,7 +739,7 @@ class Commands $maybe_service = Util::array_first(PfEnv::get_services(), function ($service) use ($sanitized_name) { foreach (["id", "zone"] as $key) { - if (!empty($service[$key])) { + if (array_key_exists($key, $service)) { return sprintf("%s%s", $service["name"], $service[$key]) == $sanitized_name; } } From e040a2dc0e92817b41c4293c1e29300ae17c4aaa Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 22:26:29 +0100 Subject: [PATCH 61/93] Fix services method call --- pfsense_zbx.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 5fc97d6..fb9eeaa 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -754,13 +754,13 @@ class Commands $short_name = $maybe_service["name"]; $carp_cfr = "$short_name."; - $is_known_service_value = array_key_exists($value, SERVICES_VALUE_ACTIONS); + $is_known_service_value = in_array($value, SERVICES_VALUE_ACTIONS); if (!$is_known_service_value) { return Util::result($maybe_service[$value], true); } return Util::result( - SERVICES_VALUE_ACTIONS[$value]( + Services::{$value}( $maybe_service, $sanitized_name, $short_name, From 0d35fdd92cab5174f666c20dff3a96327c97eb84 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 22:34:23 +0100 Subject: [PATCH 62/93] Change type hint from string to array --- pfsense_zbx.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index fb9eeaa..148bb14 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -127,24 +127,24 @@ const CARP_RES = [ class Services { - public static function enabled($service, $name, $short_name): int + public static function enabled(array $service, $name, $short_name): int { return Util::b2int(PfEnv::is_service_enabled($short_name)); } - public static function name($service, string $name): string + public static function name(array $service, string $name): string { return $name; } - public static function status(string $service): int + public static function status(array $service): int { $status = PfEnv::get_service_status($service); return empty($status) ? 0 : $status; } - public static function run_on_carp_slave($service, $name, $short_name, $carpcfr, $stopped_on_carp_slave): int + public static function run_on_carp_slave(array $service, $name, $short_name, $carpcfr, $stopped_on_carp_slave): int { return Util::b2int(in_array($carpcfr, $stopped_on_carp_slave)); } From 462389ca9d073ddd1bcdc98b8f310df31da9a442 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 22:44:19 +0100 Subject: [PATCH 63/93] Fix OpenVPN client value bug --- pfsense_zbx.php | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 148bb14..3fee12a 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -718,14 +718,11 @@ class Commands return Util::result($fallback_value, true); } - $maybe_value = $maybe_client[$value_key]; + $value = ($value_key == "status") ? + self::get_value_mapping("openvpn.client.status", $maybe_client[$value_key]) : + $maybe_client[$value_key]; - $is_known_value_key = array_key_exists($value_key, OPENVPN_CLIENT_VALUE); - if ($is_known_value_key) { - return Util::result(OPENVPN_CLIENT_VALUE[$value_key]($maybe_value), true); - } - - return Util::result(empty($maybe_value) ? $fallback_value : $maybe_value, true); + return Util::result($value == "" ? $fallback_value : $value, true); } public static function service_value(string $name, string $value) From 0aee6006e8f70bcd247406ac555f677a24af3f69 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 22:45:57 +0100 Subject: [PATCH 64/93] Output result for cert_date --- pfsense_zbx.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 3fee12a..a3b6c52 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -950,7 +950,7 @@ class Commands $cert_info = openssl_x509_parse(base64_decode($certificate[PfEnv::CRT])); return ($value == 0 || $value < $cert_info[$field]) ? $cert_info[$field] : $value; - }, 0)); + }, 0), true); } // Testing function, for template creating purpose From ce829110c7774b7f9a6f87ae90dd0dfc9fcb5df3 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 23:00:14 +0100 Subject: [PATCH 65/93] Set fallback value to 0 for openvpn_clientvalue --- pfsense_zbx.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index a3b6c52..643f527 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -709,7 +709,7 @@ class Commands return self::get_openvpn_server_uservalue_($unique_id, $value_key, "0"); } - public static function openvpn_clientvalue($client_id, $value_key, $fallback_value = "none") + public static function openvpn_clientvalue($client_id, $value_key, $fallback_value = 0) { $maybe_client = Util::array_first( PfEnv::openvpn_get_active_clients(), From 614c1721de310f13f6b2e4949f42e616a6d64075 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 23:05:23 +0100 Subject: [PATCH 66/93] Fix mapping issue in openvpn_client --- pfsense_zbx.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 643f527..969c546 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -436,7 +436,7 @@ class Discoveries { self::print_json(array_map(fn($client) => [ "{#CLIENT}" => $client["vpnid"], - "{#NAME}", self::sanitize_name($client["name"]), + "{#NAME}" => self::sanitize_name($client["name"]), ], PfEnv::openvpn_get_active_clients())); } From 9288a682d060b3da788d87e16e485f649b494f80 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 20 Feb 2022 23:59:01 +0100 Subject: [PATCH 67/93] Fix speed test --- pfsense_zbx.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 969c546..e575255 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -558,15 +558,15 @@ class SpeedTest $filename = self::if_filename($if_name); if (!file_exists($filename)) { - return; + return Util::result("", true); } $speed_test_data = json_decode(file_get_contents($filename), true); - if (array_key_exists($value, $speed_test_data)) { - return; + if (!array_key_exists($value, $speed_test_data)) { + return Util::result("", true); } - echo empty($tv1) ? $speed_test_data[$value] : $speed_test_data[$tv0][$tv1]; + return Util::result(empty($tv1) ? $speed_test_data[$tv0] : $speed_test_data[$tv0][$tv1], true); } public static function cron_install($enable = true) @@ -582,21 +582,20 @@ class SpeedTest public static function exec($if_name, $ip_address) { $output_file_path = self::if_filename($if_name); - $tmp_file_path = tempnam(sys_get_temp_dir(), ""); + $tmp_file_path = tempnam(sys_get_temp_dir(), "speedtest."); // Issue #82 // Sleep random delay in order to avoid problem when 2 pfSense on the same Internet line sleep(rand(1, 90)); $is_output_file_older_than_interval = - file_exists($output_file_path) && + !file_exists($output_file_path) || (time() - filemtime($output_file_path) > SPEED_TEST_INTERVAL_SECONDS); if (!$is_output_file_older_than_interval) { return; } - $st_command = "/usr/local/bin/speedtest --source $ip_address --json > $tmp_file_path"; - exec($st_command); + exec(implode(" ", ["/usr/local/bin/speedtest", "--source", $ip_address, "--json", ">", $tmp_file_path])); rename($tmp_file_path, $output_file_path); } From 415ce8d1a4e42fe440f153b0a0641427a2b39654 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 08:18:32 +0100 Subject: [PATCH 68/93] Fix map building in get_ipsec_status --- pfsense_zbx.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index e575255..af75d60 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1074,10 +1074,10 @@ class Commands $cname = ipsec_conid($ph1ent); } - return [ - ...$p, - $cname => $ph1ent[$ike_id], - ]; + return array_merge( + $p, + [$cname => $ph1ent[$ike_id]], + ); }, []); // Phase-Status match borrowed from status_ipsec.php From 5874bf49ad659cad6ce29b3c70212ab6f865b8ce Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 08:22:33 +0100 Subject: [PATCH 69/93] Rename symbols and reorder --- pfsense_zbx.php | 78 ++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index af75d60..b7235e4 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -26,9 +26,9 @@ require_once("services.inc"); require_once("system.inc"); require_once("util.inc"); -define("COMMAND_HANDLERS", build_method_lookup(Commands::class)); -define("DISCOVERY_SECTION_HANDLERS", build_method_lookup(Discoveries::class)); -define("SERVICES_VALUE_ACTIONS", build_method_lookup(Services::class)); +define("COMMAND_HANDLERS", build_method_lookup(Command::class)); +define("DISCOVERY_SECTION_HANDLERS", build_method_lookup(Discovery::class)); +define("SERVICES_VALUE_HANDLERS", build_method_lookup(Service::class)); define("TEXT_ACTIVE", gettext("active")); define("TEXT_DYNAMIC", gettext("dynamic")); @@ -125,31 +125,6 @@ const CARP_RES = [ CARP_MASTER => CARP_STATUS_OK ]; -class Services -{ - public static function enabled(array $service, $name, $short_name): int - { - return Util::b2int(PfEnv::is_service_enabled($short_name)); - } - - public static function name(array $service, string $name): string - { - return $name; - } - - public static function status(array $service): int - { - $status = PfEnv::get_service_status($service); - - return empty($status) ? 0 : $status; - } - - public static function run_on_carp_slave(array $service, $name, $short_name, $carpcfr, $stopped_on_carp_slave): int - { - return Util::b2int(in_array($carpcfr, $stopped_on_carp_slave)); - } -} - // Abstract undefined symbols and globals from code class PfEnv { @@ -340,7 +315,7 @@ class Util } } -class Interfaces +class NetworkInterface { public static function retrieve_wan_interfaces(): array { @@ -370,7 +345,32 @@ class Interfaces } } -class Discoveries +class Service +{ + public static function enabled(array $service, $name, $short_name): int + { + return Util::b2int(PfEnv::is_service_enabled($short_name)); + } + + public static function name(array $service, string $name): string + { + return $name; + } + + public static function status(array $service): int + { + $status = PfEnv::get_service_status($service); + + return empty($status) ? 0 : $status; + } + + public static function run_on_carp_slave(array $service, $name, $short_name, $carpcfr, $stopped_on_carp_slave): int + { + return Util::b2int(in_array($carpcfr, $stopped_on_carp_slave)); + } +} + +class Discovery { public static function gw() { @@ -546,7 +546,7 @@ class Discoveries "{#IFNAME}" => $hwif["hwif"], "{#IFDESCR}" => $hwif["description"], ]; - }, Interfaces::retrieve_wan_interfaces())); + }, NetworkInterface::retrieve_wan_interfaces())); } } @@ -616,7 +616,7 @@ class OpenVpn } } -class Commands +class Command { private const BINDING_STATES = [ "active" => [ @@ -639,7 +639,7 @@ class Commands return; } - Discoveries::{$section}(); + Discovery::{$section}(); } public static function gw_value($gw, $value_key) @@ -750,13 +750,13 @@ class Commands $short_name = $maybe_service["name"]; $carp_cfr = "$short_name."; - $is_known_service_value = in_array($value, SERVICES_VALUE_ACTIONS); + $is_known_service_value = in_array($value, SERVICES_VALUE_HANDLERS); if (!$is_known_service_value) { return Util::result($maybe_service[$value], true); } return Util::result( - Services::{$value}( + Service::{$value}( $maybe_service, $sanitized_name, $short_name, @@ -842,7 +842,7 @@ class Commands $config = PfEnv::cfg(); if ($value_key == "status") { - return Util::result(Commands::get_ipsec_status($ike_id), true); + return Util::result(Command::get_ipsec_status($ike_id), true); } if ($value_key == "disabled") { @@ -905,7 +905,7 @@ class Commands public static function speedtest_cron() { - foreach (Interfaces::retrieve_wan_interfaces() as $if_info) { + foreach (NetworkInterface::retrieve_wan_interfaces() as $if_info) { SpeedTest::exec($if_info["hwif"], $if_info["ipaddr"]); } } @@ -1371,11 +1371,11 @@ function main($arguments) $is_known_command = in_array($command, COMMAND_HANDLERS); if (!$is_known_command) { - Commands::test(); + Command::test(); exit; } - Commands::{$command}(...$parameters); + Command::{$command}(...$parameters); } main($argv); From 5c060c9185aec241aa34a48c0b97617757c758b0 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 08:29:13 +0100 Subject: [PATCH 70/93] Make Util::result echo by default --- pfsense_zbx.php | 54 ++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index b7235e4..6b4ab4d 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -295,7 +295,7 @@ class Util return (int)$b; } - public static function result($result, bool $echo_result = false) + public static function result($result, bool $echo_result = true) { if ($echo_result) { echo $result; @@ -558,15 +558,15 @@ class SpeedTest $filename = self::if_filename($if_name); if (!file_exists($filename)) { - return Util::result("", true); + return Util::result(""); } $speed_test_data = json_decode(file_get_contents($filename), true); if (!array_key_exists($value, $speed_test_data)) { - return Util::result("", true); + return Util::result(""); } - return Util::result(empty($tv1) ? $speed_test_data[$tv0] : $speed_test_data[$tv0][$tv1], true); + return Util::result(empty($tv1) ? $speed_test_data[$tv0] : $speed_test_data[$tv0][$tv1]); } public static function cron_install($enable = true) @@ -682,20 +682,20 @@ class Command { $maybe_server = Util::array_first(OpenVpn::get_active_servers(), fn($s) => $s["vpnid"] == $server_id); if (empty($maybe_server)) { - return Util::result(0, true); + return Util::result(0); } $server_value = self::get_server_value($maybe_server, $value_key); if ($value_key == "conns") { - return Util::result(is_array($server_value) ? count($server_value) : 0, true); + return Util::result(is_array($server_value) ? count($server_value) : 0); } if (in_array($value_key, ["status", "mode"])) { - return Util::result(self::get_value_mapping("openvpn.server.status", $server_value), true); + return Util::result(self::get_value_mapping("openvpn.server.status", $server_value)); } - return Util::result($server_value, true); + return Util::result($server_value); } public static function openvpn_server_uservalue($unique_id, $value_key) @@ -714,14 +714,14 @@ class Command PfEnv::openvpn_get_active_clients(), fn($client) => $client["vpnid"] == $client_id); if (empty($maybe_client)) { - return Util::result($fallback_value, true); + return Util::result($fallback_value); } $value = ($value_key == "status") ? self::get_value_mapping("openvpn.client.status", $maybe_client[$value_key]) : $maybe_client[$value_key]; - return Util::result($value == "" ? $fallback_value : $value, true); + return Util::result($value == "" ? $fallback_value : $value); } public static function service_value(string $name, string $value) @@ -744,7 +744,7 @@ class Command }); if (empty($maybe_service)) { - return Util::result("", true); + return Util::result(""); } $short_name = $maybe_service["name"]; @@ -752,7 +752,7 @@ class Command $is_known_service_value = in_array($value, SERVICES_VALUE_HANDLERS); if (!$is_known_service_value) { - return Util::result($maybe_service[$value], true); + return Util::result($maybe_service[$value]); } return Util::result( @@ -761,8 +761,7 @@ class Command $sanitized_name, $short_name, $carp_cfr, - $stopped_on_carp_slave), - true); + $stopped_on_carp_slave)); } public static function temperature($sensorid) @@ -818,19 +817,18 @@ class Command public static function system($section) { if ($section === "packages_update") { - return Util::result(self::get_outdated_packages(), true); + return Util::result(self::get_outdated_packages()); } $system_pkg_version = PfEnv::get_system_pkg_version(); if ($section === "new_version_available") { return Util::result( - Util::b2int($system_pkg_version["version"] != $system_pkg_version["installed_version"]), - true); + Util::b2int($system_pkg_version["version"] != $system_pkg_version["installed_version"])); } $is_known_section = array_key_exists($section, $system_pkg_version); - return Util::result($is_known_section ? $system_pkg_version[$section] : "", true); + return Util::result($is_known_section ? $system_pkg_version[$section] : ""); } public static function ipsec_ph1($ike_id, $value_key) @@ -842,20 +840,20 @@ class Command $config = PfEnv::cfg(); if ($value_key == "status") { - return Util::result(Command::get_ipsec_status($ike_id), true); + return Util::result(Command::get_ipsec_status($ike_id)); } if ($value_key == "disabled") { - return Util::result("0", true); + return Util::result("0"); } $maybe_ike_match = Util::array_first($config["ipsec"]["phase1"], fn($d) => $d["ikeid"] == $ike_id); if (empty($maybe_ike_match)) { - return Util::result("", true); + return Util::result(""); } if (!array_key_exists($value_key, $maybe_ike_match)) { - return Util::result("", true); + return Util::result(""); } return Util::result(self::get_value_mapping("ipsec.$value_key", $maybe_ike_match[$value_key])); @@ -878,23 +876,23 @@ class Command $maybe_data = Util::array_first($a_phase2, fn($data) => $data["uniqid"] == $uniqid); if (is_null($maybe_data) || !array_key_exists($value_key, $maybe_data)) { - return Util::result($value, true); + return Util::result($value); } $result = ($value_key != "disabled") ? self::get_value_mapping("ipsec_ph2." . $value_key, $maybe_data[$value_key]) : "1"; - return Util::result($result, true); + return Util::result($result); } public static function dhcp($section) { if ($section === "failover") { - return Util::result(self::check_dhcp_failover(), true); + return Util::result(self::check_dhcp_failover()); } - return Util::result(self::check_dhcp_offline_leases(), true); + return Util::result(self::check_dhcp_offline_leases()); } // File is present @@ -932,13 +930,13 @@ class Command return !$is_ok; }); - return Util::result($maybe_not_ok ?: SMART_OK, true); + return Util::result($maybe_not_ok ?: SMART_OK); } public static function cert_date($value_key) { if (!array_key_exists($value_key, CERT_VK_TO_FIELD)) { - return Util::result(0, true); + return Util::result(0); } $field = CERT_VK_TO_FIELD[$value_key]; From 618c9bc01062c558cfb6ffa660bb2c97317a8912 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 08:34:48 +0100 Subject: [PATCH 71/93] Use 0 as fallback for service_value --- pfsense_zbx.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 6b4ab4d..550e3e6 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -744,7 +744,7 @@ class Command }); if (empty($maybe_service)) { - return Util::result(""); + return Util::result(0); } $short_name = $maybe_service["name"]; From d76b2d0cb11820013a02fbe463f0a2a649da5a48 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 08:40:43 +0100 Subject: [PATCH 72/93] Remove redundant preg_match escape characters --- pfsense_zbx.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 550e3e6..4684100 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1160,8 +1160,8 @@ class Command private static function parse_dhcp_record(string $record): ?array { - $is_lease_record = preg_match("/^lease\s+(.*)\s+\{(.+)\}$/", $record, $lease_record_match); - $is_failover_record = preg_match("/^failover.*\"(.*)\"\s+state\s+\{(.+)\}$/", $record, $failover_record_match); + $is_lease_record = preg_match("/^lease\s+(.*)\s+{(.+)}$/", $record, $lease_record_match); + $is_failover_record = preg_match("/^failover.*\"(.*)\"\s+state\s+{(.+)}$/", $record, $failover_record_match); $is_known_record_type = $is_lease_record || $is_failover_record; if (!$is_known_record_type) { From 78f7056bb2c65bc03f095d1e4295499dd7669149 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 08:44:58 +0100 Subject: [PATCH 73/93] Replace all echos with Util::result where applicable --- pfsense_zbx.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 4684100..f729c3e 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -666,10 +666,10 @@ class Command public static function gw_status() { - echo implode(",", + return Util::result(implode(",", array_map( fn($gw) => sprintf("%s.%s", $gw["name"], $gw["status"]), - PfEnv::return_gateways_status(true))); + PfEnv::return_gateways_status(true)))); } public static function if_speedtest_value($if_name, $value) @@ -898,7 +898,7 @@ class Command // File is present public static function file_exists($filename) { - echo Util::b2int(file_exists($filename)); + return Util::result(Util::b2int(file_exists($filename))); } public static function speedtest_cron() From 9d46326dbb60cdff35aedd0f586cadea1ce4f694 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 08:56:37 +0100 Subject: [PATCH 74/93] Get rid of echo_result parameter for carp_status --- pfsense_zbx.php | 74 +++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index f729c3e..5c123a6 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -775,44 +775,11 @@ class Command echo trim($value[0]); } - public static function carp_status($echo_result = true): int + public static function carp_status(): int { - $is_carp_enabled = PfEnv::get_carp_status() != 0; - if (!$is_carp_enabled) { - return Util::result(CARP_STATUS_DISABLED, $echo_result); - } - - $is_carp_demotion_status_ok = PfEnv::get_single_sysctl("net.inet.carp.demotion") == 0; - if (!$is_carp_demotion_status_ok) { - return Util::result(CARP_STATUS_PROBLEM, $echo_result); - } - - $config = PfEnv::cfg(); - - $just_carps = array_filter($config["virtualip"]["vip"], fn($virtual_ip) => $virtual_ip["mode"] != "carp"); - $status_str = array_reduce($just_carps, function ($status, $carp) { - $if_status = PfEnv::get_carp_interface_status("_vip{$carp["uniqid"]}"); - - $state_differs_from_previous_interface = ($status != $if_status) && (!empty($if_status)); - if (!$state_differs_from_previous_interface) { - return $status; - } - - if ($status != "") { - return CARP_INCONSISTENT; - } - - return $if_status; - }, ""); - - $is_known_carp_status = array_key_exists($status_str, CARP_RES); - - return Util::result( - $is_known_carp_status ? CARP_RES[$status_str] : CARP_STATUS_UNKNOWN, - $echo_result); + return Util::result(self::get_carp_status()); } - // System Information public static function system($section) { @@ -998,6 +965,41 @@ class Command print_r(PfEnv::get_pkg_info("all", false, true)); } + private static function get_carp_status(): int + { + $is_carp_enabled = PfEnv::get_carp_status() != 0; + if (!$is_carp_enabled) { + return CARP_STATUS_DISABLED; + } + + $is_carp_demotion_status_ok = PfEnv::get_single_sysctl("net.inet.carp.demotion") == 0; + if (!$is_carp_demotion_status_ok) { + return CARP_STATUS_PROBLEM; + } + + $config = PfEnv::cfg(); + + $just_carps = array_filter($config["virtualip"]["vip"], fn($virtual_ip) => $virtual_ip["mode"] != "carp"); + $status_str = array_reduce($just_carps, function ($status, $carp) { + $if_status = PfEnv::get_carp_interface_status("_vip{$carp["uniqid"]}"); + + $state_differs_from_previous_interface = ($status != $if_status) && (!empty($if_status)); + if (!$state_differs_from_previous_interface) { + return $status; + } + + if ($status != "") { + return CARP_INCONSISTENT; + } + + return $if_status; + }, ""); + + $is_known_carp_status = array_key_exists($status_str, CARP_RES); + + return $is_known_carp_status ? CARP_RES[$status_str] : CARP_STATUS_UNKNOWN; + } + private static function get_openvpn_server_uservalue_($unique_id, $value_key, $default = "") { list($server_id, $user_id) = explode("+", $unique_id); @@ -1049,7 +1051,7 @@ class Command $v = self::get_value_mapping("ipsec.state", strtolower($r)); - $carp_status = self::carp_status(false); + $carp_status = self::get_carp_status(); return ($carp_status != 0) ? $v + (10 * ($carp_status - 1)) : $v; }; From 0d8d75a08bb5e093113f4627ba17b8ea74375569 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 09:11:42 +0100 Subject: [PATCH 75/93] Use 0 as fallback value for ipsec_ph --- pfsense_zbx.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 5c123a6..87cfd9b 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -811,16 +811,16 @@ class Command } if ($value_key == "disabled") { - return Util::result("0"); + return Util::result(0); } $maybe_ike_match = Util::array_first($config["ipsec"]["phase1"], fn($d) => $d["ikeid"] == $ike_id); if (empty($maybe_ike_match)) { - return Util::result(""); + return Util::result(0); } if (!array_key_exists($value_key, $maybe_ike_match)) { - return Util::result(""); + return Util::result(0); } return Util::result(self::get_value_mapping("ipsec.$value_key", $maybe_ike_match[$value_key])); @@ -834,7 +834,7 @@ class Command $valuecfr = explode(".", $value_key); - $value = "0"; + $value = 0; if ($valuecfr[0] == "status") { $ids = explode(".", $uniqid); $status_key = (isset($valuecfr[1])) ? $valuecfr[1] : "state"; @@ -848,7 +848,7 @@ class Command $result = ($value_key != "disabled") ? self::get_value_mapping("ipsec_ph2." . $value_key, $maybe_data[$value_key]) : - "1"; + 1; return Util::result($result); } From b838274280752504d62454ec9e459a6f6158e0e1 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 09:14:50 +0100 Subject: [PATCH 76/93] Remove redundant default_value parameter --- pfsense_zbx.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 87cfd9b..7299b0d 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -41,6 +41,8 @@ define("TEXT_RESERVED", gettext("reserved")); const SPEED_TEST_INTERVAL_HOURS = 8; const SPEED_TEST_INTERVAL_SECONDS = SPEED_TEST_INTERVAL_HOURS * 3600; +const FALLBACK_VALUE = 0; + const VALUE_MAPPINGS = [ "openvpn.server.status" => [ "down" => 0, @@ -1325,22 +1327,22 @@ class Command // Value mappings // Each value map is represented by an associative array - private static function get_value_mapping($value_name, $value, $default_value = "0") + private static function get_value_mapping($value_name, $value) { $is_known_value_name = array_key_exists($value_name, VALUE_MAPPINGS); if (!$is_known_value_name) { - return $default_value; + return FALLBACK_VALUE; } $value_mapping = VALUE_MAPPINGS[$value_name]; if (!is_array($value_mapping)) { - return $default_value; + return FALLBACK_VALUE; } $sanitized_value = strtolower($value); $is_value_with_known_mapping = array_key_exists($sanitized_value, $value_mapping); - return $is_value_with_known_mapping ? $value_mapping[$sanitized_value] : $default_value; + return $is_value_with_known_mapping ? $value_mapping[$sanitized_value] : FALLBACK_VALUE; } } From 5e5d1654ae9c7b7d2b12320c8132ea5186a2b06b Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 09:17:26 +0100 Subject: [PATCH 77/93] Use FALLBACK_VALUE const where applicable --- pfsense_zbx.php | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 7299b0d..4f03af9 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -363,7 +363,7 @@ class Service { $status = PfEnv::get_service_status($service); - return empty($status) ? 0 : $status; + return empty($status) ? FALLBACK_VALUE : $status; } public static function run_on_carp_slave(array $service, $name, $short_name, $carpcfr, $stopped_on_carp_slave): int @@ -684,13 +684,13 @@ class Command { $maybe_server = Util::array_first(OpenVpn::get_active_servers(), fn($s) => $s["vpnid"] == $server_id); if (empty($maybe_server)) { - return Util::result(0); + return Util::result(FALLBACK_VALUE); } $server_value = self::get_server_value($maybe_server, $value_key); if ($value_key == "conns") { - return Util::result(is_array($server_value) ? count($server_value) : 0); + return Util::result(is_array($server_value) ? count($server_value) : FALLBACK_VALUE); } if (in_array($value_key, ["status", "mode"])) { @@ -707,10 +707,10 @@ class Command public static function openvpn_server_uservalue_numeric($unique_id, $value_key) { - return self::get_openvpn_server_uservalue_($unique_id, $value_key, "0"); + return self::get_openvpn_server_uservalue_($unique_id, $value_key, FALLBACK_VALUE); } - public static function openvpn_clientvalue($client_id, $value_key, $fallback_value = 0) + public static function openvpn_clientvalue($client_id, $value_key, $fallback_value = FALLBACK_VALUE) { $maybe_client = Util::array_first( PfEnv::openvpn_get_active_clients(), @@ -746,7 +746,7 @@ class Command }); if (empty($maybe_service)) { - return Util::result(0); + return Util::result(FALLBACK_VALUE); } $short_name = $maybe_service["name"]; @@ -813,16 +813,16 @@ class Command } if ($value_key == "disabled") { - return Util::result(0); + return Util::result(FALLBACK_VALUE); } $maybe_ike_match = Util::array_first($config["ipsec"]["phase1"], fn($d) => $d["ikeid"] == $ike_id); if (empty($maybe_ike_match)) { - return Util::result(0); + return Util::result(FALLBACK_VALUE); } if (!array_key_exists($value_key, $maybe_ike_match)) { - return Util::result(0); + return Util::result(FALLBACK_VALUE); } return Util::result(self::get_value_mapping("ipsec.$value_key", $maybe_ike_match[$value_key])); @@ -836,7 +836,7 @@ class Command $valuecfr = explode(".", $value_key); - $value = 0; + $value = FALLBACK_VALUE; if ($valuecfr[0] == "status") { $ids = explode(".", $uniqid); $status_key = (isset($valuecfr[1])) ? $valuecfr[1] : "state"; @@ -905,7 +905,7 @@ class Command public static function cert_date($value_key) { if (!array_key_exists($value_key, CERT_VK_TO_FIELD)) { - return Util::result(0); + return Util::result(FALLBACK_VALUE); } $field = CERT_VK_TO_FIELD[$value_key]; @@ -916,7 +916,7 @@ class Command $cert_info = openssl_x509_parse(base64_decode($certificate[PfEnv::CRT])); return ($value == 0 || $value < $cert_info[$field]) ? $cert_info[$field] : $value; - }, 0), true); + }, FALLBACK_VALUE)); } // Testing function, for template creating purpose From 62882ab8fe7a683d7704239e79893e70cbe80fdd Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 09:22:09 +0100 Subject: [PATCH 78/93] Replace magic numbers with constants --- pfsense_zbx.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 4f03af9..d3400b9 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -40,6 +40,8 @@ define("TEXT_RESERVED", gettext("reserved")); const SPEED_TEST_INTERVAL_HOURS = 8; const SPEED_TEST_INTERVAL_SECONDS = SPEED_TEST_INTERVAL_HOURS * 3600; +const SPEED_TEST_RANDOM_DELAY_MIN_SECONDS = 1; +const SPEED_TEST_RANDOM_DELAY_MAX_SECONDS = 90; const FALLBACK_VALUE = 0; @@ -588,7 +590,7 @@ class SpeedTest // Issue #82 // Sleep random delay in order to avoid problem when 2 pfSense on the same Internet line - sleep(rand(1, 90)); + sleep(rand(SPEED_TEST_RANDOM_DELAY_MIN_SECONDS, SPEED_TEST_RANDOM_DELAY_MAX_SECONDS)); $is_output_file_older_than_interval = !file_exists($output_file_path) || From 15e6ad239bb8aa602cf595d30b1ab31cfa6419f6 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 09:52:29 +0100 Subject: [PATCH 79/93] Return correct SMART status --- pfsense_zbx.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index d3400b9..ee7424d 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -893,12 +893,15 @@ class Command /^SMART Health Status/ {print $2;exit}'")), PfEnv::get_smart_drive_list()); - $maybe_not_ok = Util::array_first($dev_states, function ($dev_state) { - $is_ok = - array_key_exists($dev_state, SMART_DEV_STATUS) && - SMART_DEV_STATUS[$dev_state] == SMART_OK; + $smart_states = + array_map( + fn($dev_state) => array_key_exists($dev_state, SMART_DEV_STATUS) ? + SMART_DEV_STATUS[$dev_state] : + SMART_ERROR, + $dev_states); - return !$is_ok; + $maybe_not_ok = Util::array_first($smart_states, function ($smart_state) { + return $smart_state != SMART_OK; }); return Util::result($maybe_not_ok ?: SMART_OK); From 252876cd290e72583222b16dc0b909886dd3998d Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 09:53:58 +0100 Subject: [PATCH 80/93] Swap array_first paramters --- pfsense_zbx.php | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index ee7424d..c8c1670 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -273,7 +273,7 @@ class PfEnv class Util { - public static function array_first(array $haystack, Closure $match) + public static function array_first(Closure $match, array $haystack) { foreach ($haystack as $needle) { if ($match($needle)) { @@ -449,7 +449,7 @@ class Discovery $named_services = array_filter(PfEnv::get_services(), fn($service) => !empty($service["name"])); self::print_json(array_map(function ($service) { - $maybe_id = Util::array_first(array_keys($service), fn($key) => in_array($key, ["id", "zone"])); + $maybe_id = Util::array_first(fn($key) => in_array($key, ["id", "zone"]), array_keys($service)); $id = is_null($maybe_id) ? "" : $service[$maybe_id]; return [ @@ -684,7 +684,7 @@ class Command public static function openvpn_servervalue(int $server_id, $value_key) { - $maybe_server = Util::array_first(OpenVpn::get_active_servers(), fn($s) => $s["vpnid"] == $server_id); + $maybe_server = Util::array_first(fn($s) => $s["vpnid"] == $server_id, OpenVpn::get_active_servers()); if (empty($maybe_server)) { return Util::result(FALLBACK_VALUE); } @@ -715,8 +715,8 @@ class Command public static function openvpn_clientvalue($client_id, $value_key, $fallback_value = FALLBACK_VALUE) { $maybe_client = Util::array_first( - PfEnv::openvpn_get_active_clients(), - fn($client) => $client["vpnid"] == $client_id); + fn($client) => $client["vpnid"] == $client_id, + PfEnv::openvpn_get_active_clients()); if (empty($maybe_client)) { return Util::result($fallback_value); } @@ -737,7 +737,7 @@ class Command // Waiting for a way in Zabbix to use Global Regexp in triggers with items discovery $stopped_on_carp_slave = array("haproxy", "radvd", "openvpn.", "openvpn", "avahi"); - $maybe_service = Util::array_first(PfEnv::get_services(), function ($service) use ($sanitized_name) { + $maybe_service = Util::array_first(function ($service) use ($sanitized_name) { foreach (["id", "zone"] as $key) { if (array_key_exists($key, $service)) { return sprintf("%s%s", $service["name"], $service[$key]) == $sanitized_name; @@ -745,7 +745,7 @@ class Command } return $service["name"] == $sanitized_name; - }); + }, PfEnv::get_services()); if (empty($maybe_service)) { return Util::result(FALLBACK_VALUE); @@ -818,7 +818,7 @@ class Command return Util::result(FALLBACK_VALUE); } - $maybe_ike_match = Util::array_first($config["ipsec"]["phase1"], fn($d) => $d["ikeid"] == $ike_id); + $maybe_ike_match = Util::array_first(fn($d) => $d["ikeid"] == $ike_id, $config["ipsec"]["phase1"]); if (empty($maybe_ike_match)) { return Util::result(FALLBACK_VALUE); } @@ -845,7 +845,7 @@ class Command $value = self::get_ipsec_status($ids[0], $ids[1], $status_key); } - $maybe_data = Util::array_first($a_phase2, fn($data) => $data["uniqid"] == $uniqid); + $maybe_data = Util::array_first(fn($data) => $data["uniqid"] == $uniqid, $a_phase2); if (is_null($maybe_data) || !array_key_exists($value_key, $maybe_data)) { return Util::result($value); } @@ -900,9 +900,9 @@ class Command SMART_ERROR, $dev_states); - $maybe_not_ok = Util::array_first($smart_states, function ($smart_state) { + $maybe_not_ok = Util::array_first(function ($smart_state) { return $smart_state != SMART_OK; - }); + }, $smart_states); return Util::result($maybe_not_ok ?: SMART_OK); } @@ -1013,12 +1013,12 @@ class Command $servers = OpenVpn::get_active_servers(); - $maybe_server = Util::array_first($servers, fn($server) => $server["vpnid"] == $server_id); + $maybe_server = Util::array_first(fn($server) => $server["vpnid"] == $server_id, $servers); if (!$maybe_server) { return $default; } - $maybe_conn = Util::array_first($maybe_server["conns"], fn($conn) => ($conn["common_name"] == $user_id)); + $maybe_conn = Util::array_first(fn($conn) => ($conn["common_name"] == $user_id), $maybe_server["conns"]); return $maybe_conn[$value_key] ?: $default; } @@ -1088,7 +1088,7 @@ class Command }, []); // Phase-Status match borrowed from status_ipsec.php - $maybe_ike_sa = Util::array_first($ipsec_list_sa, function ($ike_sa) use ($ike_id, $connection_map) { + $maybe_ike_sa = Util::array_first(function ($ike_sa) use ($ike_id, $connection_map) { $con_id = isset($ike_sa["con-id"]) ? substr($ike_sa["con-id"], 3) : filter_var($ike_id, FILTER_SANITIZE_NUMBER_INT); @@ -1101,7 +1101,7 @@ class Command $ph1idx = ($is_version_1 || $is_split_connection) ? $connection_map[$con_name] : $con_id; return $ph1idx == $ike_id; - }); + }, $ipsec_list_sa); if (!$maybe_ike_sa) { return $process_result($value_key, $result); From 7f22ed2971f036146219f088abacf6041c528ad9 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 10:18:42 +0100 Subject: [PATCH 81/93] Contain all calls to shell scripts in class --- pfsense_zbx.php | 90 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index c8c1670..e022198 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -349,6 +349,61 @@ class NetworkInterface } } +class Shell +{ + const ARP = "/usr/sbin/arp"; + const AWK = "/usr/bin/awk"; + const CAT = "/bin/cat"; + const SMART_CTL = "/usr/local/sbin/smartctl"; + const SPEED_TEST = "/usr/local/bin/speedtest"; + + public static function run_speed_test(string $source_ip_address, string $output_file_path) + { + $tmp_file_path = tempnam(sys_get_temp_dir(), "speedtest."); + + exec(implode(" ", [self::SPEED_TEST, "--source", $source_ip_address, "--json", ">", $tmp_file_path])); + + rename($tmp_file_path, $output_file_path); + } + + public static function retrieve_smart_status(string $device_name): string + { + return trim(exec( + implode(" ", + [ + self::SMART_CTL, "-H", "/dev/$device_name", "|", + self::AWK, "-F:", "'/^SMART overall-health self-assessment test result/ {print $2;exit} / + ^SMART Health Status/ {print $2;exit}'"]))); + } + + public static function parse_dhcpd_records(string $leases_file_path): array + { + // Remove all content up to the first lease record + $clean_pattern = "'/lease.*{\$/,0'"; + + // Split file into records by '}' + $split_pattern = "'BEGIN { RS=ORS=\"}\" } { gsub(\"\\n\", \"\"); print; printf \"\\n\"}'"; + + // Stuff the leases file in a proper format into an array by line + exec( + implode(" ", + [ + self::CAT, $leases_file_path, "2>/dev/null", "|", + self::AWK, $clean_pattern, "|", + self::AWK, $split_pattern]), $raw_lease_records); + + return $raw_lease_records; + } + + public static function retrieve_arp_ips(): array + { + exec(implode(" ", [self::ARP, "-an", "|", + self::AWK, "'{ gsub(/[()]/,\"\") } {print $2}'"]), $arp_data); + + return $arp_data; + } +} + class Service { public static function enabled(array $service, $name, $short_name): int @@ -583,10 +638,9 @@ class SpeedTest true); } - public static function exec($if_name, $ip_address) + public static function run($if_name, $ip_address) { $output_file_path = self::if_filename($if_name); - $tmp_file_path = tempnam(sys_get_temp_dir(), "speedtest."); // Issue #82 // Sleep random delay in order to avoid problem when 2 pfSense on the same Internet line @@ -599,8 +653,7 @@ class SpeedTest return; } - exec(implode(" ", ["/usr/local/bin/speedtest", "--source", $ip_address, "--json", ">", $tmp_file_path])); - rename($tmp_file_path, $output_file_path); + Shell::run_speed_test($ip_address, $output_file_path); } private static function if_filename($if_name): string @@ -875,7 +928,7 @@ class Command public static function speedtest_cron() { foreach (NetworkInterface::retrieve_wan_interfaces() as $if_info) { - SpeedTest::exec($if_info["hwif"], $if_info["ipaddr"]); + SpeedTest::run($if_info["hwif"], $if_info["ipaddr"]); } } @@ -889,8 +942,7 @@ class Command public static function smart_status() { $dev_states = array_map( - fn($dev) => trim(exec("smartctl -H /dev/$dev | awk -F: '/^SMART overall-health self-assessment test result/ {print $2;exit} -/^SMART Health Status/ {print $2;exit}'")), + fn($device_name) => Shell::retrieve_smart_status($device_name), PfEnv::get_smart_drive_list()); $smart_states = @@ -1184,20 +1236,9 @@ class Command return self::parse_failover_record(array_slice($failover_record_match, 1)); } - private static function read_dhcp_records_from_file(string $leases_file): array + private static function read_dhcp_records_from_file(string $leases_file_path): array { - $awk = "/usr/bin/awk"; - - // Remove all content up to the first lease record - $clean_pattern = "'/lease.*{\$/,0'"; - - // Split file into records by '}' - $split_pattern = "'BEGIN { RS=ORS=\"}\" } { gsub(\"\\n\", \"\"); print; printf \"\\n\"}'"; - - // Stuff the leases file in a proper format into an array by line - exec( - "/bin/cat $leases_file 2>/dev/null | $awk $clean_pattern | $awk $split_pattern", - $raw_lease_records); + $raw_lease_records = Shell::parse_dhcpd_records($leases_file_path); $relevant_records = array_filter($raw_lease_records, fn($r) => preg_match("/^lease.*|^failover.*/", $r)); @@ -1270,13 +1311,6 @@ class Command return compact("name", "mystate", "peerstate", "mydate", "peerdate"); } - private static function arp_ips() - { - exec("/usr/sbin/arp -an | awk '{ gsub(/[()]/,\"\") } {print $2}'", $arp_data); - - return $arp_data; - } - // Get DHCP Arrays (copied from status_dhcp_leases.php, waiting for pfsense 2.5, in order to use system_get_dhcpleases();) private static function get_dhcp($value_key): array { @@ -1299,7 +1333,7 @@ class Command $lease_records = array_filter($dhcp_records, fn($r) => $r["type"] == "lease"); - $arp_ips = self::arp_ips(); + $arp_ips = Shell::retrieve_arp_ips(); return self::remove_duplicates(array_map(fn($r) => self::raw_lease_record_to_lease($r, $arp_ips), $lease_records), "mac"); } From fc6f0b0cd2eda494a913c6726a99bd30f7049261 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 10:27:33 +0100 Subject: [PATCH 82/93] Rename and simplify some Shell methods --- pfsense_zbx.php | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index e022198..7586003 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -366,7 +366,7 @@ class Shell rename($tmp_file_path, $output_file_path); } - public static function retrieve_smart_status(string $device_name): string + public static function read_smart_status(string $device_name): string { return trim(exec( implode(" ", @@ -376,7 +376,7 @@ class Shell ^SMART Health Status/ {print $2;exit}'"]))); } - public static function parse_dhcpd_records(string $leases_file_path): array + public static function read_dhcpd_records(string $leases_file_path): array { // Remove all content up to the first lease record $clean_pattern = "'/lease.*{\$/,0'"; @@ -392,10 +392,10 @@ class Shell self::AWK, $clean_pattern, "|", self::AWK, $split_pattern]), $raw_lease_records); - return $raw_lease_records; + return array_filter($raw_lease_records, fn($r) => preg_match("/^lease.*|^failover.*/", $r)); } - public static function retrieve_arp_ips(): array + public static function read_arp_ips(): array { exec(implode(" ", [self::ARP, "-an", "|", self::AWK, "'{ gsub(/[()]/,\"\") } {print $2}'"]), $arp_data); @@ -942,7 +942,7 @@ class Command public static function smart_status() { $dev_states = array_map( - fn($device_name) => Shell::retrieve_smart_status($device_name), + fn($device_name) => Shell::read_smart_status($device_name), PfEnv::get_smart_drive_list()); $smart_states = @@ -1238,11 +1238,9 @@ class Command private static function read_dhcp_records_from_file(string $leases_file_path): array { - $raw_lease_records = Shell::parse_dhcpd_records($leases_file_path); - - $relevant_records = array_filter($raw_lease_records, fn($r) => preg_match("/^lease.*|^failover.*/", $r)); - - return array_map(fn($r) => self::parse_dhcp_record($r), $relevant_records); + return array_map( + fn($r) => self::parse_dhcp_record($r), + Shell::read_dhcpd_records($leases_file_path)); } private static function binding_to_state($binding): array @@ -1333,7 +1331,7 @@ class Command $lease_records = array_filter($dhcp_records, fn($r) => $r["type"] == "lease"); - $arp_ips = Shell::retrieve_arp_ips(); + $arp_ips = Shell::read_arp_ips(); return self::remove_duplicates(array_map(fn($r) => self::raw_lease_record_to_lease($r, $arp_ips), $lease_records), "mac"); } From 286aee913c262fbc32e5a487ef9c555b9351a769 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 10:34:19 +0100 Subject: [PATCH 83/93] Fix indenting --- pfsense_zbx.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 7586003..a9f351e 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -397,7 +397,8 @@ class Shell public static function read_arp_ips(): array { - exec(implode(" ", [self::ARP, "-an", "|", + exec(implode(" ", [ + self::ARP, "-an", "|", self::AWK, "'{ gsub(/[()]/,\"\") } {print $2}'"]), $arp_data); return $arp_data; From dd578c70c891745707731457b2119e9aecfb00f4 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 10:36:21 +0100 Subject: [PATCH 84/93] Change variable to const --- pfsense_zbx.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index a9f351e..40cf9cd 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -45,6 +45,8 @@ const SPEED_TEST_RANDOM_DELAY_MAX_SECONDS = 90; const FALLBACK_VALUE = 0; +const LINE = "-------------------\n"; + const VALUE_MAPPINGS = [ "openvpn.server.status" => [ "down" => 0, @@ -980,17 +982,16 @@ class Command // Testing function, for template creating purpose public static function test() { - $line = "-------------------\n"; $config = PfEnv::cfg(); echo "OPENVPN Servers:\n"; print_r(OpenVpn::get_active_servers()); - echo $line; + echo LINE; echo "OPENVPN Clients:\n"; print_r(PfEnv::openvpn_get_active_clients()); - echo $line; + echo LINE; $ifdescrs = PfEnv::get_configured_interface_with_descr(true); $ifaces = []; @@ -1001,11 +1002,11 @@ class Command print_r($ifaces); print_r(PfEnv::get_interface_arr()); print_r(PfEnv::get_configured_interface_list()); - echo $line; + echo LINE; echo "Services: \n"; print_r(PfEnv::get_services()); - echo $line; + echo LINE; echo "IPsec: \n"; PfEnv::init_config_arr(array("ipsec", "phase1")); @@ -1019,7 +1020,7 @@ class Command echo "IPsec Config Phase 2: \n"; print_r($config["ipsec"]["phase2"]); - echo $line; + echo LINE; echo "Packages: \n"; print_r(PfEnv::get_pkg_info("all", false, true)); From 32a42bcad40d27ce7deb741b5b6ff3c1df8f109b Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Mon, 21 Feb 2022 12:26:43 +0100 Subject: [PATCH 85/93] Fallback to empty array when null --- pfsense_zbx.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 40cf9cd..e6d45d9 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -199,12 +199,12 @@ class PfEnv public static function get_services() { - return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()) ?: []; } public static function get_smart_drive_list() { - return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()) ?: []; } public static function get_ipsecifnum() @@ -244,7 +244,7 @@ class PfEnv public static function ipsec_list_sa() { - return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()) ?: []; } public static function is_service_enabled() @@ -254,12 +254,12 @@ class PfEnv public static function openvpn_get_active_clients() { - return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()) ?: []; } public static function openvpn_get_active_servers() { - return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()) ?: []; } public static function return_gateways_status() @@ -862,9 +862,9 @@ class Command { // Get Value from IPsec Phase 1 Configuration // If Getting "disabled" value only check item presence in config array - PfEnv::init_config_arr(["ipsec", "phase1"]); - $config = PfEnv::cfg(); + PfEnv::init_config_arr(["ipsec", "phase1"]); + $a_phase2 = $config["ipsec"]["phase1"] ?: []; if ($value_key == "status") { return Util::result(Command::get_ipsec_status($ike_id)); @@ -874,7 +874,7 @@ class Command return Util::result(FALLBACK_VALUE); } - $maybe_ike_match = Util::array_first(fn($d) => $d["ikeid"] == $ike_id, $config["ipsec"]["phase1"]); + $maybe_ike_match = Util::array_first(fn($d) => $d["ikeid"] == $ike_id, $a_phase2); if (empty($maybe_ike_match)) { return Util::result(FALLBACK_VALUE); } @@ -890,7 +890,7 @@ class Command { $config = PfEnv::cfg(); PfEnv::init_config_arr(array("ipsec", "phase2")); - $a_phase2 = &$config["ipsec"]["phase2"]; + $a_phase2 = $config["ipsec"]["phase2"] ?: []; $valuecfr = explode(".", $value_key); @@ -1072,7 +1072,7 @@ class Command return $default; } - $maybe_conn = Util::array_first(fn($conn) => ($conn["common_name"] == $user_id), $maybe_server["conns"]); + $maybe_conn = Util::array_first(fn($conn) => ($conn["common_name"] == $user_id), $maybe_server["conns"] ?: []); return $maybe_conn[$value_key] ?: $default; } From 3996432202ec4816285a7c0a7a1cfc9707d70fdc Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Tue, 22 Feb 2022 14:00:27 +0100 Subject: [PATCH 86/93] Fix gateway discovery --- pfsense_zbx.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index e6d45d9..7174903 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -438,7 +438,7 @@ class Discovery { self::print_json(array_map( fn($gw) => ["{#GATEWAY}" => $gw["name"]], - PfEnv::return_gateways_status(true))); + array_values(PfEnv::return_gateways_status(true)))); } public static function wan() From 56ee15d4837b55d6aea26d8b4c3b67788fdc08ea Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Wed, 23 Feb 2022 09:00:51 +0100 Subject: [PATCH 87/93] Reinstate exclusive running of speedtests --- pfsense_zbx.php | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 7174903..822c559 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -645,10 +645,6 @@ class SpeedTest { $output_file_path = self::if_filename($if_name); - // Issue #82 - // Sleep random delay in order to avoid problem when 2 pfSense on the same Internet line - sleep(rand(SPEED_TEST_RANDOM_DELAY_MIN_SECONDS, SPEED_TEST_RANDOM_DELAY_MAX_SECONDS)); - $is_output_file_older_than_interval = !file_exists($output_file_path) || (time() - filemtime($output_file_path) > SPEED_TEST_INTERVAL_SECONDS); @@ -656,7 +652,30 @@ class SpeedTest return; } - Shell::run_speed_test($ip_address, $output_file_path); + self::run_exclusively(function () use ($ip_address, $output_file_path) { + // Issue #82 + // Sleep random delay in order to avoid problem when 2 pfSense on the same Internet line + sleep(rand(SPEED_TEST_RANDOM_DELAY_MIN_SECONDS, SPEED_TEST_RANDOM_DELAY_MAX_SECONDS)); + + Shell::run_speed_test($ip_address, $output_file_path); + }); + } + + private static function run_exclusively(Closure $fn) + { + $fp = fopen(implode("", [sys_get_temp_dir(), "pfz-speedtest.lock"]), "w"); + + $is_other_test_currently_running = !flock($fp, LOCK_EX | LOCK_NB); + if ($is_other_test_currently_running) { + fclose($fp); + return; + } + + $fn(); + + flock($fp, LOCK_UN); + + fclose($fp); } private static function if_filename($if_name): string @@ -915,11 +934,9 @@ class Command public static function dhcp($section) { - if ($section === "failover") { - return Util::result(self::check_dhcp_failover()); - } - - return Util::result(self::check_dhcp_offline_leases()); + return Util::result(($section === "failover") ? + self::check_dhcp_failover() : + self::check_dhcp_offline_leases()); } // File is present @@ -982,7 +999,6 @@ class Command // Testing function, for template creating purpose public static function test() { - $config = PfEnv::cfg(); echo "OPENVPN Servers:\n"; From 66f4615423a3b880bbb1ae5e3af8bbd3ca03684f Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Wed, 2 Mar 2022 11:19:35 +0100 Subject: [PATCH 88/93] Fix cert_date when no cert or ca available --- pfsense_zbx.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 822c559..8f95fb3 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -288,7 +288,7 @@ class Util public static function array_flatten(array $multi_dimensional_array): array { - return array_merge(...$multi_dimensional_array); + return array_merge(...$multi_dimensional_array) ?: []; } public static function array_zip(array $keys, array $values): array @@ -987,7 +987,7 @@ class Command $field = CERT_VK_TO_FIELD[$value_key]; $config = PfEnv::cfg(); - $all_certs = Util::array_flatten(array_map(fn($cert_type) => $config[$cert_type], ["cert", "ca"])); + $all_certs = Util::array_flatten(array_map(fn($cert_type) => $config[$cert_type] ?: [], ["cert", "ca"])); return Util::result(array_reduce($all_certs, function ($value, $certificate) use ($field) { $cert_info = openssl_x509_parse(base64_decode($certificate[PfEnv::CRT])); From e603ca1a7bffa8fe369957c010fcf8165c5b5c84 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Wed, 29 Jun 2022 14:50:39 +0200 Subject: [PATCH 89/93] fix: use 'state' for vpn 'status' check on 22.05+ --- pfsense_zbx.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 8f95fb3..4908af8 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -56,6 +56,7 @@ const VALUE_MAPPINGS = [ "waiting" => 4, "server_user_listening" => 5], "openvpn.client.status" => [ + "connected" => 1, "up" => 1, "down" => 0, "none" => 0, @@ -797,7 +798,7 @@ class Command } $value = ($value_key == "status") ? - self::get_value_mapping("openvpn.client.status", $maybe_client[$value_key]) : + self::sanitize_openvpn_clientvalue_status($maybe_client) : $maybe_client[$value_key]; return Util::result($value == "" ? $fallback_value : $value); @@ -1042,6 +1043,16 @@ class Command print_r(PfEnv::get_pkg_info("all", false, true)); } + private static function sanitize_openvpn_clientvalue_status($client_data) + { + $is_pre_version_22_05 = in_array($client_data["status"], array_keys(VALUE_MAPPINGS["openvpn.client.status"])); + + $raw_value = $is_pre_version_22_05 ? + $client_data["status"] : $client_data["state"]; + + return self::get_value_mapping("openvpn.client.status", $raw_value); + } + private static function get_carp_status(): int { $is_carp_enabled = PfEnv::get_carp_status() != 0; From 40c5ca87cdd3dcce1d22ee47cf258d85cd872bf3 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 25 Sep 2022 20:13:29 +0200 Subject: [PATCH 90/93] Replace array_map with foreach --- pfsense_zbx.php | 219 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 147 insertions(+), 72 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 4908af8..5907921 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -292,11 +292,6 @@ class Util return array_merge(...$multi_dimensional_array) ?: []; } - public static function array_zip(array $keys, array $values): array - { - return array_map(null, $keys, $values); - } - public static function b2int(bool $b): int { return (int)$b; @@ -326,16 +321,14 @@ class NetworkInterface { public static function retrieve_wan_interfaces(): array { - $if_descriptions = PfEnv::get_configured_interface_with_descr(true); + $interfaces = []; - $interfaces = array_map(function ($interface) { - list ($if_name, $description) = $interface; - - return array_merge( + foreach (PfEnv::get_configured_interface_with_descr(true) as $if_name => $description) { + $interfaces[] = array_merge( PfEnv::get_interface_info($if_name), ["description" => $description], ); - }, Util::array_zip(array_keys($if_descriptions), array_values($if_descriptions))); + } return array_filter($interfaces, function ($iface_info_ext) { $has_gw = array_key_exists("gateway", $iface_info_ext); @@ -437,9 +430,12 @@ class Discovery { public static function gw() { - self::print_json(array_map( - fn($gw) => ["{#GATEWAY}" => $gw["name"]], - array_values(PfEnv::return_gateways_status(true)))); + $gateway_discoveries = []; + foreach (PfEnv::return_gateways_status(true) as $gateway) { + $gateway_discoveries[] = ["{#GATEWAY}" => $gateway["name"]]; + } + + self::print_json($gateway_discoveries); } public static function wan() @@ -475,10 +471,15 @@ class Discovery public static function openvpn_server() { - self::print_json(array_map(fn($server) => [ - "{#SERVER}" => $server["vpnid"], - "{#NAME}" => self::sanitize_name($server["name"])], - OpenVpn::get_active_servers())); + $active_openvpn_server_discoveries = []; + foreach (OpenVpn::get_active_servers() as $server) { + $active_openvpn_server_discoveries[] = [ + "{#SERVER}" => $server["vpnid"], + "{#NAME}" => self::sanitize_name($server["name"]), + ]; + } + + self::print_json($active_openvpn_server_discoveries); } public static function openvpn_server_user() @@ -492,30 +493,43 @@ class Discovery $servers_with_relevant_mode, fn($server) => is_array($server["conns"])); - self::print_json(Util::array_flatten(array_map(fn($s) => self::map_server($s), $servers_with_conns))); + $server_discoveries = []; + foreach ($servers_with_conns as $server) { + $server_discoveries[] = self::map_server($server); + } + + self::print_json(Util::array_flatten($server_discoveries)); } public static function openvpn_client() { - self::print_json(array_map(fn($client) => [ - "{#CLIENT}" => $client["vpnid"], - "{#NAME}" => self::sanitize_name($client["name"]), - ], PfEnv::openvpn_get_active_clients())); + $client_discoveries = []; + foreach (PfEnv::openvpn_get_active_clients() as $client) { + $client_discoveries[] = [ + "{#CLIENT}" => $client["vpnid"], + "{#NAME}" => self::sanitize_name($client["name"]), + ]; + } + + self::print_json($client_discoveries); } public static function services() { $named_services = array_filter(PfEnv::get_services(), fn($service) => !empty($service["name"])); - self::print_json(array_map(function ($service) { + $service_discoveries = []; + foreach ($named_services as $service) { $maybe_id = Util::array_first(fn($key) => in_array($key, ["id", "zone"]), array_keys($service)); $id = is_null($maybe_id) ? "" : $service[$maybe_id]; - return [ + $service_discoveries[] = [ "{#SERVICE}" => sprintf("%s%s", Util::space_to_underscore($service["name"]), $id), "{#DESCRIPTION}" => $service["description"], ]; - }, $named_services)); + } + + self::print_json($service_discoveries); } public static function interfaces() @@ -529,10 +543,15 @@ class Discovery $config = PfEnv::cfg(); - self::print_json(array_map(fn($data) => [ - "{#IKEID}" => $data["ikeid"], - "{#NAME}" => $data["descr"], - ], $config["ipsec"]["phase1"])); + $ipsec_ph1_discoveries = []; + foreach ($config["ipsec"]["phase1"] as $data) { + $ipsec_ph1_discoveries[] = [ + "{#IKEID}" => $data["ikeid"], + "{#NAME}" => $data["descr"], + ]; + } + + self::print_json($ipsec_ph1_discoveries); } public static function ipsec_ph2() @@ -541,13 +560,18 @@ class Discovery $config = PfEnv::cfg(); - self::print_json(array_map(fn($data) => [ - "{#IKEID}" => $data["ikeid"], - "{#NAME}" => $data["descr"], - "{#UNIQID}" => $data["uniqid"], - "{#REQID}" => $data["reqid"], - "{#EXTID}" => sprintf("%s.%s", $data["ikeid"], $data["reqid"]), - ], $config["ipsec"]["phase2"])); + $ipsec_ph2_discoveries = []; + foreach ($config["ipsec"]["phase2"] as $data) { + $ipsec_ph2_discoveries[] = [ + "{#IKEID}" => $data["ikeid"], + "{#NAME}" => $data["descr"], + "{#UNIQID}" => $data["uniqid"], + "{#REQID}" => $data["reqid"], + "{#EXTID}" => sprintf("%s.%s", $data["ikeid"], $data["reqid"]), + ]; + } + + self::print_json($ipsec_ph2_discoveries); } public static function dhcpfailover() @@ -555,9 +579,14 @@ class Discovery // System public static functions regarding DHCP Leases will be available in the upcoming release of pfSense, so let's wait $leases = PfEnv::system_get_dhcpleases(); - self::print_json(array_map(fn($data) => [ - "{#FAILOVER_GROUP}" => Util::space_to_underscore($data["name"]), - ], $leases["failover"])); + $dhcp_failover_discoveries = []; + foreach ($leases["failover"] as $data) { + $dhcp_failover_discoveries[] = [ + "{#FAILOVER_GROUP}" => Util::space_to_underscore($data["name"]), + ]; + } + + self::print_json($dhcp_failover_discoveries); } private static function print_json(array $json) @@ -584,9 +613,13 @@ class Discovery private static function map_conns(string $server_name, string $vpn_id, array $conns): array { - return array_map( - fn($conn) => self::map_conn($server_name, $vpn_id, $conn), - $conns); + $conn_discoveries = []; + foreach ($conns as $conn) { + $conn_discoveries[] = self::map_conn($server_name, $vpn_id, $conn); + + } + + return $conn_discoveries; } private static function map_server(array $server): array @@ -604,12 +637,15 @@ class Discovery return; } - self::print_json(array_map(function ($hwif) { - return [ + $wan_interface_discoveries = []; + foreach (NetworkInterface::retrieve_wan_interfaces() as $hwif) { + $wan_interface_discoveries[] = [ "{#IFNAME}" => $hwif["hwif"], "{#IFDESCR}" => $hwif["description"], ]; - }, NetworkInterface::retrieve_wan_interfaces())); + } + + self::print_json($wan_interface_discoveries); } } @@ -746,10 +782,12 @@ class Command public static function gw_status() { - return Util::result(implode(",", - array_map( - fn($gw) => sprintf("%s.%s", $gw["name"], $gw["status"]), - PfEnv::return_gateways_status(true)))); + $gw_statuses = []; + foreach (PfEnv::return_gateways_status(true) as $gw) { + $gw_statuses[] = sprintf("%s.%s", $gw["name"], $gw["status"]); + } + + return Util::result(implode(",", $gw_statuses)); } public static function if_speedtest_value($if_name, $value) @@ -962,16 +1000,17 @@ class Command // Taken from /usr/local/www/widgets/widgets/smart_status.widget.php public static function smart_status() { - $dev_states = array_map( - fn($device_name) => Shell::read_smart_status($device_name), - PfEnv::get_smart_drive_list()); + $dev_states = []; + foreach (PfEnv::get_smart_drive_list() as $device_name) { + $dev_states[] = Shell::read_smart_status($device_name); + } - $smart_states = - array_map( - fn($dev_state) => array_key_exists($dev_state, SMART_DEV_STATUS) ? - SMART_DEV_STATUS[$dev_state] : - SMART_ERROR, - $dev_states); + $smart_states = []; + foreach ($dev_states as $dev_state) { + $smart_states[] = array_key_exists($dev_state, SMART_DEV_STATUS) ? + SMART_DEV_STATUS[$dev_state] : + SMART_ERROR; + } $maybe_not_ok = Util::array_first(function ($smart_state) { return $smart_state != SMART_OK; @@ -988,7 +1027,12 @@ class Command $field = CERT_VK_TO_FIELD[$value_key]; $config = PfEnv::cfg(); - $all_certs = Util::array_flatten(array_map(fn($cert_type) => $config[$cert_type] ?: [], ["cert", "ca"])); + + $certs_and_cas = []; + foreach (["cert", "ca"] as $cert_type) { + $certs_and_cas[] = $config[$cert_type] ?: []; + } + $all_certs = Util::array_flatten($certs_and_cas); return Util::result(array_reduce($all_certs, function ($value, $certificate) use ($field) { $cert_info = openssl_x509_parse(base64_decode($certificate[PfEnv::CRT])); @@ -1213,8 +1257,12 @@ class Command private static function parse_raw_record(string $raw_lease_data): array { - $lease_data_lines = - array_filter(array_map(fn($m) => trim($m), explode(";", $raw_lease_data))); + $raw_lease_data_lines = []; + foreach (explode(";", $raw_lease_data) as $raw_line) { + $raw_lease_data_lines[] = trim($raw_line); + } + + $lease_data_lines = array_filter($raw_lease_data_lines); return array_reduce( $lease_data_lines, @@ -1228,7 +1276,12 @@ class Command private static function parse_failover_record(array $data): array { - list($name, $raw_lease_data) = array_map(fn($m) => trim($m), $data); + $sanitized_data = []; + foreach ($data as $line) { + $sanitized_data[] = trim($line); + } + + list($name, $raw_lease_data) = $sanitized_data; return [ "type" => "failover", @@ -1239,7 +1292,12 @@ class Command private static function parse_lease_record(array $data): array { - list($lease_address, $raw_lease_data) = array_map(fn($m) => trim($m), $data); + $sanitized_data = []; + foreach ($data as $line) { + $sanitized_data[] = trim($line); + } + + list($lease_address, $raw_lease_data) = $sanitized_data; return [ "type" => "lease", @@ -1267,9 +1325,13 @@ class Command private static function read_dhcp_records_from_file(string $leases_file_path): array { - return array_map( - fn($r) => self::parse_dhcp_record($r), - Shell::read_dhcpd_records($leases_file_path)); + $dhcp_records = []; + foreach (Shell::read_dhcpd_records($leases_file_path) as $raw_dhcp_record) { + $dhcp_records[] = self::parse_dhcp_record($raw_dhcp_record); + + } + + return $dhcp_records; } private static function binding_to_state($binding): array @@ -1355,14 +1417,22 @@ class Command if ($value_key === "pools") { $failover_records = array_filter($dhcp_records, fn($r) => $r["type"] == "failover"); - return self::remove_duplicates(array_map(fn($r) => self::raw_failover_record_to_pool($r), $failover_records), "name"); - } + $pools = []; + foreach ($failover_records as $failover_record) { + $pools[] = self::raw_failover_record_to_pool($failover_record); + } - $lease_records = array_filter($dhcp_records, fn($r) => $r["type"] == "lease"); + return self::remove_duplicates($pools, "name"); + } $arp_ips = Shell::read_arp_ips(); - return self::remove_duplicates(array_map(fn($r) => self::raw_lease_record_to_lease($r, $arp_ips), $lease_records), "mac"); + $leases = []; + foreach (array_filter($dhcp_records, fn($r) => $r["type"] == "lease") as $lease_record) { + $leases[] = self::raw_lease_record_to_lease($lease_record, $arp_ips); + } + + return self::remove_duplicates($leases, "mac"); } private static function check_dhcp_offline_leases(): int @@ -1421,7 +1491,12 @@ function build_method_lookup(string $clazz): array $commands = array_filter($all_methods, fn($method) => $method->isStatic() && $method->isPublic()); - return array_map(fn(ReflectionMethod $method) => $method->getName(), $commands); + $available_methods = []; + foreach ($commands as $method) { + $available_methods[] = $method->getName(); + } + + return $available_methods; } catch (Exception $e) { return []; } From be58c15c5cf8af0c21e68f65b46f08f11dff65f1 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 25 Sep 2022 21:28:47 +0200 Subject: [PATCH 91/93] Replace ternary with explicit if statements --- pfsense_zbx.php | 75 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index 5907921..d919352 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -417,7 +417,11 @@ class Service { $status = PfEnv::get_service_status($service); - return empty($status) ? FALLBACK_VALUE : $status; + if (!empty($status)) { + return $status; + } + + return FALLBACK_VALUE; } public static function run_on_carp_slave(array $service, $name, $short_name, $carpcfr, $stopped_on_carp_slave): int @@ -521,7 +525,11 @@ class Discovery $service_discoveries = []; foreach ($named_services as $service) { $maybe_id = Util::array_first(fn($key) => in_array($key, ["id", "zone"]), array_keys($service)); - $id = is_null($maybe_id) ? "" : $service[$maybe_id]; + + $id = ""; + if (!is_null($maybe_id)) { + $id = $service[$maybe_id]; + } $service_discoveries[] = [ "{#SERVICE}" => sprintf("%s%s", Util::space_to_underscore($service["name"]), $id), @@ -665,7 +673,11 @@ class SpeedTest return Util::result(""); } - return Util::result(empty($tv1) ? $speed_test_data[$tv0] : $speed_test_data[$tv0][$tv1]); + if (empty($tv1)) { + return Util::result($speed_test_data[$tv0]); + } + + return Util::result($speed_test_data[$tv0][$tv1]); } public static function cron_install($enable = true) @@ -761,18 +773,18 @@ class Command public static function gw_value($gw, $value_key) { $gws = PfEnv::return_gateways_status(true); - - $maybe_gw = array_key_exists($gw, $gws) ? $gws[$gw] : null; - if (!$maybe_gw) { + if (!array_key_exists($gw, $gws)) { return Util::result(""); } - $value = $maybe_gw[$value_key]; + $gw_data = $gws[$gw]; + + $value = $gw_data[$value_key]; if ($value_key != "status") { return Util::result($value); } - $substatus = $maybe_gw["substatus"]; + $substatus = $gw_data["substatus"]; $has_relevant_substatus = $substatus != "none"; // Issue #70: Gateway Forced Down return Util::result(self::get_value_mapping( @@ -839,7 +851,11 @@ class Command self::sanitize_openvpn_clientvalue_status($maybe_client) : $maybe_client[$value_key]; - return Util::result($value == "" ? $fallback_value : $value); + if ($value == "") { + return Util::result($fallback_value); + } + + return Util::result($value); } public static function service_value(string $name, string $value) @@ -913,7 +929,11 @@ class Command $is_known_section = array_key_exists($section, $system_pkg_version); - return Util::result($is_known_section ? $system_pkg_version[$section] : ""); + if ($is_known_section) { + return Util::result($$system_pkg_version[$section]); + } + + return Util::result(""); } public static function ipsec_ph1($ike_id, $value_key) @@ -964,18 +984,21 @@ class Command return Util::result($value); } - $result = ($value_key != "disabled") ? - self::get_value_mapping("ipsec_ph2." . $value_key, $maybe_data[$value_key]) : - 1; + if ($value_key != "disabled") { + return Util::result(self::get_value_mapping("ipsec_ph2." . $value_key, $maybe_data[$value_key])); + } - return Util::result($result); + return Util::result(1); } public static function dhcp($section) { - return Util::result(($section === "failover") ? - self::check_dhcp_failover() : - self::check_dhcp_offline_leases()); + if ($section === "failover") { + return Util::result(self::check_dhcp_failover()); + + } + + return Util::result(self::check_dhcp_offline_leases()); } // File is present @@ -1037,7 +1060,11 @@ class Command return Util::result(array_reduce($all_certs, function ($value, $certificate) use ($field) { $cert_info = openssl_x509_parse(base64_decode($certificate[PfEnv::CRT])); - return ($value == 0 || $value < $cert_info[$field]) ? $cert_info[$field] : $value; + if ($value == 0 || $value < $cert_info[$field]) { + return $cert_info[$field]; + } + + return $value; }, FALLBACK_VALUE)); } @@ -1185,7 +1212,11 @@ class Command $carp_status = self::get_carp_status(); - return ($carp_status != 0) ? $v + (10 * ($carp_status - 1)) : $v; + if ($carp_status != 0) { + return $v + (10 * ($carp_status - 1)); + } + + return $v; }; $ipsec_list_sa = PfEnv::ipsec_list_sa(); @@ -1478,7 +1509,11 @@ class Command $sanitized_value = strtolower($value); $is_value_with_known_mapping = array_key_exists($sanitized_value, $value_mapping); - return $is_value_with_known_mapping ? $value_mapping[$sanitized_value] : FALLBACK_VALUE; + if ($is_value_with_known_mapping) { + return $value_mapping[$sanitized_value]; + } + + return FALLBACK_VALUE; } } From 5265b64ca3faab27a726658bff5e07d88639bf63 Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 27 Nov 2022 14:33:50 +0100 Subject: [PATCH 92/93] fix: ipsec status dectection --- pfsense_zbx.php | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index d919352..e3827ec 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -1201,14 +1201,12 @@ class Command { PfEnv::init_config_arr(array("ipsec", "phase1")); - $result = ""; - $process_result = function ($vk, $r) { if ($vk != "state") { return $r; } - $v = self::get_value_mapping("ipsec.state", strtolower($r)); + $v = self::get_value_mapping("ipsec.state", strtolower($r[$vk])); $carp_status = self::get_carp_status(); @@ -1221,7 +1219,7 @@ class Command $ipsec_list_sa = PfEnv::ipsec_list_sa(); if (!is_array($ipsec_list_sa)) { - return $process_result($value_key, $result); + return 0; } $config = PfEnv::cfg(); @@ -1239,10 +1237,11 @@ class Command return array_merge( $p, - [$cname => $ph1ent[$ike_id]], + [$cname => $ike_id], ); }, []); + // Phase-Status match borrowed from status_ipsec.php $maybe_ike_sa = Util::array_first(function ($ike_sa) use ($ike_id, $connection_map) { $con_id = isset($ike_sa["con-id"]) ? @@ -1260,15 +1259,25 @@ class Command }, $ipsec_list_sa); if (!$maybe_ike_sa) { - return $process_result($value_key, $result); + return 0; + } + + if ($req_id == -1) { + return $process_result($value_key, $maybe_ike_sa); } $just_matching_child_sas = array_filter($maybe_ike_sa["child-sas"], fn($child_sa) => ($child_sa["reqid"] == $req_id)); + if (count($just_matching_child_sas) === 0) { + return 0; + } + + $result = NULL; + // Asking for Phase2 Status Value foreach ($just_matching_child_sas as $child_sa) { - $result = $child_sa[$value_key]; + $result = $child_sa; // If state is rekeyed go on if (strtolower($child_sa["state"]) == "rekeyed") { From 88c7cb2abf47a8c06eddd029b0557898faea8f0f Mon Sep 17 00:00:00 2001 From: Ely Deckers Date: Sun, 27 Nov 2022 15:24:06 +0100 Subject: [PATCH 93/93] refactor: move ipsec_conid to PvEnv class --- pfsense_zbx.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pfsense_zbx.php b/pfsense_zbx.php index e3827ec..bd82c12 100644 --- a/pfsense_zbx.php +++ b/pfsense_zbx.php @@ -213,6 +213,11 @@ class PfEnv return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); } + public static function ipsec_conid() + { + return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); + } + public static function get_pkg_info() { return self::call_pfsense_method_with_same_name_and_arguments(func_get_args()); @@ -1232,7 +1237,7 @@ class Command $cname = $id_name ? "con$id_name" : "con{$ike_id}00000"; } else { - $cname = ipsec_conid($ph1ent); + $cname = PfEnv::ipsec_conid($ph1ent); } return array_merge(