Popisovaný příklad vychází z praktické situace, kterou řeším. Využívám description portu, kam zapisuji co je do daného portu zapojeno - označení zásuvky (když je připojena zásuvka) nebo jméno počítače (pokud je přímo připojen do portu). Potom je jednoduché zjistit, kam port vede (například, když sleduji nějakou podezřelou MAC adresu v síti), aniž bych musel procházet kabely. Občas, ale potřebuji opačnou věc, pro určitou zásuvku nalézt switch a port, kam je zapojena. Proto potřebuji seznam všech portů a switchů, který se však ručně složitě udržuje. A tady nastupuje myšlenka získávat tyto informace dynamicky pomocí SNMP, na což mě přivedlo Cacti, kde v podstatě tato možnost již je.
PHP a podpora SNMP
Pozn.: Používám PHP (a Apache) pod Windows, takže můj popis tomu odpovídá. Pod Linuxem jsou některé věci odlišné.
V programovacím jazyce PHP máme k dispozici extension php_snmp, které nabízí základní SNMP komunikační příkazy jako snmpget
, snmpset
a snmpwalk
. Některé další příkazy jsou dostupné až od verze PHP 5. Podrobný popis naleznete v dokumentaci http://www.php.net/manual/en/ref.snmp.php .
Omezením je, že tyto příkazy používají pouze SNMP verze 1. Našel jsem různé odkazy na nedokumentované funkce snmp2_get
, snmp3_get
, apod., které by měly používat SNMPv2c a SNMPv3. Ale tyto funkce v mé verzi PHP nefungují.
PHP má ve svém adresáři podadresář mibs, který obsahuje běžné MIB tabulky. Může však nastat problém, že PHP tento adresář nenajde a při použití SNMP vypisuje varování (a samozřejmě pak MIB databázi nevyužívá). Řešením je vytvořit systémovou proměnnou (Control Panel - System - Advanced - Environment Variables - System variables) se jménem MIBDIRS a hodnotou je cesta k adresáři s MIB soubory.
Pozn.: Například v EasyPHP není adresář s MIB soubory obsažen, a proto je třeba jej vykopírovat z běžné instalace PHP.
Výhodou, když používáme MIB tabulky je, že návratové hodnoty jsou podle nich upraveny a například výčtový typ má doplněno jméno (to je například u typu interfacu).
V PHP pro SNMP existuje také řada funkcí, které nastavují chování hodnot, například, že se OID zobrazují číselně nebo výsledná hodnota neobsahuje doplňující informace (jako třeba typ - Gauge32: 100). Bohužel ne vše vždy správně funguje.
Druhou možností pro použití SNMP je mít nainstalováno Net-SNMP a volat jeho příkazy z PHP pomocí funkce
exec
a přebírat výstup. V tomto případě můžeme využít všechny verze SNMP a nemáme problém s verzemi PHP (které obsahují pouze některé funkce).
SNMP pro interfacy
SNMP hodnoty pro interfacy jsou součástí standardní MIB tabulky IF-MIB. Strom hodnot začíná na OID = 1.3.6.1.2.1.2, zápis pomocí jmen uzlů je iso.org.dod.internet.mgmt.mib-2.interfaces
. První hodnotou je počet interfaců.
jméno | OID | popis |
---|---|---|
ifNumber | .1.3.6.1.2.1.2.1.0 | počet interfaců daného zařízení |
Dále je tabulka, která obsahuje určité vlastnosti pro každý interface. Zajímavé jsou například
jméno | OID | popis |
---|---|---|
ifIndex | .1.3.6.1.2.1.2.2.1.1 | indexové číslo interfacu |
ifDescr | .1.3.6.1.2.1.2.2.1.2 | pojmenování portu |
ifType | .1.3.6.1.2.1.2.2.1.3 | typ portu |
ifSpeed | .1.3.6.1.2.1.2.2.1.5 | rychlost portu |
ifOperStatus | .1.3.6.1.2.1.2.2.1.8 | stav portu (up, down, ...) |
ifInUcastPkts | .1.3.6.1.2.1.2.2.1.11 | příchozí unikástové pakety |
Některé další hodnoty nalezneme na trochu jiném místě, pod OID = 1.3.6.1.2.1.31, iso.org.dod.internet.mgmt.mib-2.ifMIB
jméno | OID | popis |
---|---|---|
ifName | .1.3.6.1.2.1.31.1.1.1.1 | zkrácené jméno portu |
ifAlias | .1.3.6.1.2.1.31.1.1.1.18 | popis (alias) portu |
Informace o IP adresách (ty mohou být přiřazeny samozřejmě jen někde, např. pro VLANy) jsou pod OID = 1.3.6.1.2.1.4, iso.org.dod.internet.mgmt.mib-2.ip
jméno | OID | popis |
---|---|---|
ipAdEntAddr | .1.3.6.1.2.1.4.20.1.2 | IP adresy |
ipAdEntIfIndex | .1.3.6.1.2.1.4.20.1.1 | indexy interfaců pro IP adresy |
Pro switch jsou také zajímavé globální informace jako jméno a umístění. To jsou hodnoty, které můžeme zadat téměř pro každé SNMP zařízení při instalaci (nebo v nastavení). OID = .1.3.6.1.2.1.1, iso.org.dod.internet.mgmt.mib-2.system
jméno | OID | popis |
---|---|---|
sysName | .1.3.6.1.2.1.1.5.0 | jméno zařízení |
sysLocation | .1.3.6.1.2.1.1.6.0 | umístění |
Některé údaje, jako třeba počet interfaců nebo jméno zařízení jsou reprezentovány jednou hodnotou a můžeme proto využít funkci snmpget
. Ostatní hodnoty, které jsou specifické pro každý interface, jsou uloženy tak, že za dané OID je připojen index interfacu a teprve zde je uložena hodnota. Protože tyto interfacy většinou a neznáme a také proto, že je to efektivnější, můžeme využít funkci snmpwalk
, která projde všechny hodnoty pod zadaným OID a vrátí nám pole hodnot. Vhodnější je ještě funkce snmprealwalk
, která vrací také OID pro danou hodnotu.
Můžeme také využít funkci snmpwalk
tak, že ji zavoláme na OID pro interface a získáme všechny hodnoty pro všechny interfacy.
Ralizace v PHP
// funkce, která naplní pole $interfaces hodnotami ze zařízení s adresou $ip a pro přístup se použije community string $comm function getInterface(&$interfaces, $ip, $comm) { getInterPart($interfaces, $ip, $comm, ".1.3.6.1.2.1.2.2.1.1", "Index"); getInterPart($interfaces, $ip, $comm, ".1.3.6.1.2.1.2.2.1.8", "Status"); getInterPart($interfaces, $ip, $comm, ".1.3.6.1.2.1.2.2.1.2", "Port_name"); getInterPart($interfaces, $ip, $comm, ".1.3.6.1.2.1.31.1.1.1.18", "Descr"); getInterPart($interfaces, $ip, $comm, ".1.3.6.1.2.1.2.2.1.3", "Type"); $name = format_snmp_string(snmpget($ip, $comm, ".1.3.6.1.2.1.1.5.0")); addInterSwitcheVal($interfaces, "Switch", $name); } // do pole přidá položku, která je stejná pro všechny indexy function addInterSwitcheVal(&$interfaces, $valname, $value) { foreach($interfaces as $val) $interfaces[$val["Index"]][$valname] = $value; } // načte jednu vlastnost pro všechny interfacy function getInterPart(&$interfaces, $ip, $comm, $oid, $name) { $array = snmprealwalk($ip, $comm, $oid); foreach($array as $key => $val) { $index = ereg_replace('.*\.([0-9]+)$', "\\1", $key); //index je posledni cislo z OID $val = format_snmp_string($val); $interfaces[$index][$name] = $val; } } // vytiskne pole interfaců jako tabulku function printInterfaces($interfaces) { echo "<table>"; echo "<tr><th>Switch</th><th>Index</th><th>Status</th><th>Port_name</th><th>descr</th><th>Type</th> tr>"; foreach($interfaces as $val) { echo "<tr>"; echo "<td>".$val["Switch"]."</td>"; echo "<td>".$val["Index"]."</td>"; echo "<td>".$val["Status"]."</td>"; echo "<td>".$val["Port_name"]."</td>"; echo "<td>".$val["Descr"]."</td>"; echo "<td>".$val["Type"]."</td>"; echo "</tr>"; } echo "</table>"; } // proměnná kam se ukládají výsledné hodnoty, jendá se o dvourozměrné pole - všechny interfacy (podle indexu) a načtené hodnoty (podle jména) $interfaces = array(); $ip = "10.0.0.1"; $co = "public"; // IP adresa zařízení, community string getInterface($interfaces, $ip, $co); printInterface($interfaces);
Uvedený příklad je pouze nástin řešení. Je třeba jej obalit do HTML tagů, aby se mohl použít jako webová stránka. Také by si zasloužil řadu vylepšení nebo se dá řešit úplně jiným způsobem. Výsledné hodnoty můžeme například ukládat do databáze, což stačí jednou za delší dobu, protože asi nedochází k tolik častým změnám. Standardně jsou návratové hodnoty obaleny do řady zbytečných informaci (číselné hodnoty, MAC adresa ..), takže je vhodné je odstranit. Například Cisco switche vrací 3 typy interfaců - vlastní porty, VLANy a null interface. Takže zobrazovat třeba chceme pouze porty, což můžeme filtrovat podle ifType
.
Můj příklad sloužil hlavně k tomu uvědomit si, jaké jsou možnosti a ukázat jak je jednoduché využívat SNMP z PHP.
diky moc