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'); //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 //Testing function, for template creating purpose function pfz_test(){ $line = "-------------------\n"; $ovpn_servers = pfz_openvpn_get_all_servers(); echo "OPENVPN Servers:\n"; print_r($ovpn_servers); echo $line; $ovpn_clients = openvpn_get_active_clients(); echo "OPENVPN Clients:\n"; 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; } echo "Network Interfaces:\n"; print_r($ifaces); print_r(get_interface_arr()); print_r(get_configured_interface_list()); echo $line; $services = get_services(); echo "Services: \n"; print_r($services); echo $line; echo "IPsec: \n"; require_once("ipsec.inc"); global $config; init_config_arr(array('ipsec', 'phase1')); init_config_arr(array('ipsec', 'phase2')); $a_phase2 = &$config['ipsec']['phase2']; $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); } // Interface Discovery // Improved performance function pfz_interface_discovery() { $ifdescrs = get_configured_interface_with_descr(true); $ifaces = get_interface_arr(); $ifcs=array(); $json_string = '['; foreach ($ifdescrs as $ifname => $ifdescr){ $ifinfo = get_interface_info($ifname); $ifinfo["description"] = $ifdescr; $ifcs[$ifname] = $ifinfo; } foreach ($ifaces as $hwif) { $json_string .= '{"{#IFNAME}":"' . $hwif . '"'; $ifdescr = $hwif; foreach($ifcs as $ifc=>$ifinfo){ if ($ifinfo["hwif"] == $hwif){ $ifdescr = $ifinfo["description"]; break; } } $json_string .= ',"{#IFDESCR}":"' . $ifdescr . '"'; $json_string .= '},'; } $json_string = rtrim($json_string,","); $json_string .= "]"; echo $json_string; } // 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 = '['; 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; } // 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_valuemap("openvpn.server.status", $value); break; case "mode": $value = pfz_valuemap("openvpn.server.mode", $value); break; } //if ($value=="") $value="none"; echo $value; } //OpenVPN Server/User-Auth Discovery function pfz_openvpn_server_userdiscovery(){ $servers = pfz_openvpn_get_all_servers(); $json_string = '['; foreach ($servers as $server){ if ( ($server['mode']=='server_user') || ($server['mode']=='server_tls_user') ) { if (is_array($server['conns'])) { $name = trim(preg_replace('/\w{3}(\d)?\:\d{4,5}/i', '', $server['name'])); foreach($server['conns'] as $conn) { $json_string .= '{"{#SERVERID}":"' . $server['vpnid'] . '"'; $json_string .= ',"{#SERVERNAME}":"' . $name . '"'; $json_string .= ',"{#UNIQUEID}":"' . $server['vpnid'] . '+' . $conn['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=""){ $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 = '['; 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_openvpn_clientvalue($client_id, $valuekey, $default="none"){ $clients = openvpn_get_active_clients(); foreach($clients as $client) { if($client['vpnid']==$client_id) $value = $client[$valuekey]; } switch ($valuekey){ case "status": $value = pfz_valuemap("openvpn.client.status", $value); break; } if ($value=="") $value=$default; echo $value; } // Services Discovery // 2020-03-27: Added space replace with __ for issue #12 function pfz_services_discovery(){ $services = get_services(); $json_string = '['; foreach ($services as $service){ if (!empty($service['name'])) { // IPerf is only started on demand if ($service['name'] == "iperf") continue; $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","openvpn.","openvpn"); foreach ($services as $service){ $namecfr = $service["name"]; $carpcfr = $service["name"]; //OpenVPN if (!empty($service['id'])) { $namecfr = $service['name'] . "." . $service["id"]; $carpcfr = $service['name'] . "."; } //Captive Portal if (!empty($service['zone'])) { $namecfr = $service['name'] . "." . $service["zone"]; $carpcfr = $service['name'] . "."; } if ($namecfr == $name){ switch ($value) { case "status": $status = get_service_status($service); if ($status=="") $status = 0; echo $status; break; case "name": echo $namecfr; break; case "enabled": if (is_service_enabled($service['name'])) echo 1; else echo 0; break; case "run_on_carp_slave": if (in_array($carpcfr,$stopped_on_carp_slave)) echo 0; else echo 1; break; default: echo $service[$value]; break; } } } } //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,","); } function pfz_gw_discovery() { $gws = return_gateways_status(true); $json_string = '['; 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") $value = pfz_valuemap("gateway.status", $value); echo $value; } } // IPSEC Discovery function pfz_ipsec_discovery_ph1(){ require_once("ipsec.inc"); global $config; init_config_arr(array('ipsec', 'phase1')); $a_phase1 = &$config['ipsec']['phase1']; $json_string = '['; 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($ikeid,$valuekey){ // Get Value from IPsec Phase 1 Configuration // If Getting "disabled" value only check item presence in config array require_once("ipsec.inc"); global $config; init_config_arr(array('ipsec', 'phase1')); $a_phase1 = &$config['ipsec']['phase1']; $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_valuemap("ipsec." . $valuekey, $data[$valuekey], $data[$valuekey]); break; } } } } echo $value; } function pfz_ipsec_discovery_ph2(){ require_once("ipsec.inc"); global $config; init_config_arr(array('ipsec', 'phase2')); $a_phase2 = &$config['ipsec']['phase2']; $json_string = '['; 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"); global $config; 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_valuemap("ipsec_ph2." . $valuekey, $data[$valuekey], $data[$valuekey]); break; } } } echo $value; } function pfz_ipsec_status($ikeid,$reqid=-1,$valuekey='state'){ require_once("ipsec.inc"); global $config; init_config_arr(array('ipsec', 'phase1')); $a_phase1 = &$config['ipsec']['phase1']; $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($l_ikeid, FILTER_SANITIZE_NUMBER_INT); } if ($ikesa['version'] == 1) { $ph1idx = substr($con_id, 0, strrpos(substr($con_id, 0, -1), '00')); $ipsecconnected[$ph1idx] = $ph1idx; } else { if (!ipsec_ikeid_used($con_id)) { // probably a v2 with split connection then $ph1idx = substr($con_id, 0, strrpos(substr($con_id, 0, -1), '00')); $ipsecconnected[$ph1idx] = $ph1idx; } else { $ipsecconnected[$con_id] = $ph1idx = $con_id; } } if ($ph1idx == $ikeid){ if ($reqid!=-1) { // Asking for Phase2 Status Value foreach ($ikesa['child-sas'] as $childsas) { if ($childsas['reqid']==$reqid) { if ($childsas['state'] == 'REKEYED') { //if state is rekeyed go on $tmp_value = $childsas[$valuekey]; } else { $tmp_value = $childsas[$valuekey]; break; } } } } else { $tmp_value = $ikesa[$valuekey]; } break; } } } switch($valuekey) { case 'state': $value = pfz_valuemap('ipsec.state', strtolower($tmp_value)); if ($carp_status != 0) $value = $value + (10 * ($carp_status-1)); break; default: $value = $tmp_value; break; } // print_r($ikesa); return $value; } function pfz_carp_status($echo = true){ //Detect CARP Status global $config; $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; } 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 = '['; 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; } //Packages function pfz_packages_uptodate(){ require_once("pkg-utils.inc"); $installed_packages = get_pkg_info('all', false, true); $ret = 0; foreach ($installed_packages as $package){ if ($package['version']!=$package['installed_version']){ $ret ++; } } 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(); if ($pkgver['version']==$pkgver['installed_version']) echo "0"; else echo "1"; break; case "packages_update": echo pfz_packages_uptodate(); break; } } // Value mappings // Each value map is represented by an associative array function pfz_valuemap($valuename, $value, $default="0"){ switch ($valuename){ 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; 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( "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; case "ipsec.state": $valuemap = array ( "established" => 1, "connecting" => 2, "installed" => 1, "rekeyed" => 2); break; } if (array_key_exists($value, $valuemap)) return $valuemap[$value]; return $default; } //Argument parsers for Discovery function pfz_discovery($section){ switch (strtolower($section)){ case "gw": pfz_gw_discovery(); 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; } } //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 "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; default: pfz_test(); }