The described example is based on a practical situation that I am addressing. I use the port description, where I record what is connected to a given port - the socket label (when a socket is connected) or the computer name (if it is directly connected to the port). Then it is easy to find out where the port leads (for example, when I'm tracking a suspicious MAC address on the network), without having to go through the cables. Occasionally, however, I need the opposite, to find the switch and port where a certain socket is connected. For this, I need a list of all ports and switches, which is difficult to maintain manually. And this is where the idea of dynamically obtaining this information using SNMP comes in, which was brought to me by Cacti, where this feature essentially already exists.
PHP and SNMP Support
Note: I am using PHP (and Apache) on Windows, so my description reflects that. Some things are different under Linux.
In the programming language PHP, we have the php_snmp extension, which offers basic SNMP communication commands like snmpget, snmpset, and snmpwalk. Some additional commands are available only from PHP 5 onwards. Detailed description can be found in the documentation https://www.php.net/manual/en/ref.snmp.php.
The limitation is that these commands use only SNMP version 1. I've found various references to undocumented functions like snmp2_get, snmp3_get, etc., which should use SNMPv2c and SNMPv3. But these functions do not work in my PHP version.
PHP has a subdirectory mibs in its directory, which contains common MIB tables. However, it can happen that PHP does not find this directory, and when using SNMP, it displays a warning (and of course, then does not use the MIB database). The solution is to create a system variable (Control Panel - System - Advanced - Environment Variables - System variables) named MIBDIRS and set its value to the path to the directory with MIB files.
Note: For example, in EasyPHP, the directory with MIB files is not included, so it needs to be copied from a regular PHP installation.
The advantage of using MIB tables is that the return values are adjusted according to them, and for example, an enumeration type has the name added (this is the case for the interface type).
In PHP for SNMP, there are also a number of functions that set the behavior of the values, for example, that OIDs are displayed numerically or that the resulting value does not contain additional information (such as the type - Gauge32: 100). Unfortunately, not everything always works correctly.
Another option for using SNMP is to have Net-SNMP installed and call its commands from PHP using the exec function and capture the output. In this case, we can use all versions of SNMP and do not have the problem with PHP versions (which only contain some functions).
SNMP for Interfaces
SNMP values for interfaces are part of the standard MIB table IF-MIB. The value tree starts at OID = 1.3.6.1.2.1.2, written using node names as iso.org.dod.internet.mgmt.mib-2.interfaces. The first value is the number of interfaces.
| Name | OID | Description |
|---|---|---|
| ifNumber | .1.3.6.1.2.1.2.1.0 | Number of interfaces on the device |
There is also a table that contains certain properties for each interface. Of interest are, for example:
| Name | OID | Description |
|---|---|---|
| ifIndex | .1.3.6.1.2.1.2.2.1.1 | Interface index number |
| ifDescr | .1.3.6.1.2.1.2.2.1.2 | Port name |
| ifType | .1.3.6.1.2.1.2.2.1.3 | Port type |
| ifSpeed | .1.3.6.1.2.1.2.2.1.5 | Port speed |
| ifOperStatus | .1.3.6.1.2.1.2.2.1.8 | Port status (up, down, ...) |
| ifInUcastPkts | .1.3.6.1.2.1.2.2.1.11 | Incoming unicast packets |
Some additional values can be found in a slightly different location, under OID = 1.3.6.1.2.1.31, iso.org.dod.internet.mgmt.mib-2.ifMIB
| Name | OID | Description |
|---|---|---|
| ifName | .1.3.6.1.2.1.31.1.1.1.1 | Short port name |
| ifAlias | .1.3.6.1.2.1.31.1.1.1.18 | Port description (alias) |
Information about IP addresses (which can be assigned only in some places, e.g. for VLANs) is under OID = 1.3.6.1.2.1.4, iso.org.dod.internet.mgmt.mib-2.ip
| Name | OID | Description |
|---|---|---|
| ipAdEntAddr | .1.3.6.1.2.1.4.20.1.2 | IP addresses |
| ipAdEntIfIndex | .1.3.6.1.2.1.4.20.1.1 | Interface indices for IP addresses |
For switches, global information like name and location is also interesting. These are values that can be set for almost any SNMP device during installation (or in the settings). OID = .1.3.6.1.2.1.1, iso.org.dod.internet.mgmt.mib-2.system
| Name | OID | Description |
|---|---|---|
| sysName | .1.3.6.1.2.1.1.5.0 | Device name |
| sysLocation | .1.3.6.1.2.1.1.6.0 | Location |
Some values, such as the number of interfaces or the device name, are represented by a single value, and we can therefore use the snmpget function. Other values that are specific to each interface are stored in a way that the index of the interface is appended to the given OID, and the value is stored here. Since we usually don't know these interfaces and it's also more efficient, we can use the snmpwalk function, which goes through all the values under the given OID and returns an array of values to us. The better function is snmprealwalk, which also returns the OID for the given value.
We can also use the snmpwalk function in a way that we call it on the OID for the interface and get all the values for all interfaces.
Implementation in PHP
// function that fills the $interfaces array with values from the device at address $ip using the 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); } // adds an item to the array that is the same for all indices function addInterSwitcheVal(&$interfaces, $valname, $value) { foreach($interfaces as $val) $interfaces[$val["Index"]][$valname] = $value; } // loads one property for all interfaces 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 is the last number from the OID $val = format_snmp_string($val); $interfaces[$index][$name] = $val; } } // prints the interfaces array as a table 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>"; } // variable where the resulting values are stored, it is a two-dimensional array - all interfaces (by index) and the loaded values (by name) $interfaces = array(); $ip = "10.0.0.1"; $co = "public"; // device IP address, community string getInterface($interfaces, $ip, $co); printInterface($interfaces);
The example provided is just a sketch of the solution. It needs to be wrapped in HTML tags to be usable as a web page. It would also deserve a number of improvements or could be solved in a completely different way. We could, for example, store the resulting values in a database, which would only need to be done occasionally, as the changes are not too frequent. Normally, the return values are wrapped in a series of unnecessary information (numeric values, MAC address, etc.), so it's appropriate to remove them. For example, Cisco switches return 3 types of interfaces - physical ports, VLANs, and null interfaces. So we might want to display only the ports, which we can filter by ifType.
My example was mainly intended to make you aware of the possibilities and show how simple it is to use SNMP from PHP.
diky moc ;-)