diff --git a/front/plugins/snmp_discovery/README.md b/front/plugins/snmp_discovery/README.md index 69742770..94f88cff 100755 --- a/front/plugins/snmp_discovery/README.md +++ b/front/plugins/snmp_discovery/README.md @@ -6,7 +6,7 @@ A plugin for importing devices from an SNMP-enabled router or switch. Using SNMP Specify the following settings in the Settings section of NetAlertX: -- `SNMPDSC_routers` - A list of `snmpwalk` commands to execute against IP addresses of routers/switches with SNMP turned on. For example: +- `SNMPDSC_routers` - A list of `snmpwalk` commands to execute against IP addresses of routers/switches with SNMP turned on. For example: - `snmpwalk -v 2c -c public -OXsq 192.168.1.1 .1.3.6.1.2.1.3.1.1.2` - `snmpwalk -v 2c -c public -Oxsq 192.168.1.1 .1.3.6.1.2.1.3.1.1.2` (note: lower case `x`) @@ -14,6 +14,14 @@ Specify the following settings in the Settings section of NetAlertX: If unsure, please check [snmpwalk examples](https://www.comparitech.com/net-admin/snmpwalk-examples-windows-linux/). +Supported output formats: + +``` +ipNetToMediaPhysAddress[3][192.168.1.9] 6C:6C:6C:6C:6C:b6C1 +IP-MIB::ipNetToMediaPhysAddress.17.10.10.3.202 = STRING: f8:81:1a:ef:ef:ef +mib-2.3.1.1.2.15.1.192.168.1.14 "2C F4 32 18 61 43 " +``` + ### Setup Cisco IOS Enable IOS SNMP service and restrict to selected (internal) IP/Subnet. diff --git a/front/plugins/snmp_discovery/script.py b/front/plugins/snmp_discovery/script.py index 0dd92b06..e14d6802 100755 --- a/front/plugins/snmp_discovery/script.py +++ b/front/plugins/snmp_discovery/script.py @@ -30,7 +30,7 @@ RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log') def main(): - mylog('verbose', ['[SNMPDSC] In script ']) + mylog('verbose', f"[{pluginName}] In script ") # init global variables global snmpWalkCmds @@ -57,7 +57,7 @@ def main(): commands = [snmpWalkCmds] for cmd in commands: - mylog('verbose', ['[SNMPDSC] Router snmpwalk command: ', cmd]) + mylog('verbose', [f"[{pluginName}] Router snmpwalk command: ", cmd]) # split the string, remove white spaces around each item, and exclude any empty strings snmpwalkArgs = [arg.strip() for arg in cmd.split(' ') if arg.strip()] @@ -72,7 +72,7 @@ def main(): timeout=(timeoutSetting) ) - mylog('verbose', ['[SNMPDSC] output: ', output]) + mylog('verbose', [f"[{pluginName}] output: ", output]) lines = output.split('\n') @@ -80,6 +80,8 @@ def main(): tmpSplt = line.split('"') + # Expected Format: + # mib-2.3.1.1.2.15.1.192.168.1.14 "2C F4 32 18 61 43 " if len(tmpSplt) == 3: ipStr = tmpSplt[0].split('.')[-4:] # Get the last 4 elements to extract the IP @@ -89,7 +91,7 @@ def main(): macAddress = ':'.join(macStr) ipAddress = '.'.join(ipStr) - mylog('verbose', [f'[SNMPDSC] IP: {ipAddress} MAC: {macAddress}']) + mylog('verbose', [f"[{pluginName}] IP: {ipAddress} MAC: {macAddress}"]) plugin_objects.add_object( primaryId = handleEmpty(macAddress), @@ -100,8 +102,40 @@ def main(): foreignKey = handleEmpty(macAddress) # Use the primary ID as the foreign key ) else: - mylog('verbose', ['[SNMPDSC] ipStr does not seem to contain a valid IP:', ipStr]) + mylog('verbose', [f"[{pluginName}] ipStr does not seem to contain a valid IP:", ipStr]) + # Expected Format: + # IP-MIB::ipNetToMediaPhysAddress.17.10.10.3.202 = STRING: f8:81:1a:ef:ef:ef + elif "ipNetToMediaPhysAddress" in line and "=" in line and "STRING:" in line: + + # Split on "=" → ["IP-MIB::ipNetToMediaPhysAddress.xxx.xxx.xxx.xxx ", " STRING: aa:bb:cc:dd:ee:ff"] + left, right = line.split("=", 1) + + # Extract the MAC (right side) + macAddress = right.split("STRING:")[-1].strip() + macAddress = normalize_mac(macAddress) + + # Extract IP address from the left side + # tail of the OID: last 4 integers = IPv4 address + oid_parts = left.strip().split('.') + ip_parts = oid_parts[-4:] + ipAddress = ".".join(ip_parts) + + mylog('verbose', [f"[{pluginName}] (fallback) IP: {ipAddress} MAC: {macAddress}"]) + + plugin_objects.add_object( + primaryId = handleEmpty(macAddress), + secondaryId = handleEmpty(ipAddress), + watched1 = '(unknown)', + watched2 = handleEmpty(snmpwalkArgs[6]), + extra = handleEmpty(line), + foreignKey = handleEmpty(macAddress) + ) + + continue + + # Expected Format: + # ipNetToMediaPhysAddress[3][192.168.1.9] 6C:6C:6C:6C:6C:b6C1 elif line.startswith('ipNetToMediaPhysAddress'): # Format: snmpwalk -OXsq output parts = line.split() @@ -110,7 +144,7 @@ def main(): ipAddress = parts[0].split('[')[-1][:-1] macAddress = normalize_mac(parts[1]) - mylog('verbose', [f'[SNMPDSC] IP: {ipAddress} MAC: {macAddress}']) + mylog('verbose', [f"[{pluginName}] IP: {ipAddress} MAC: {macAddress}"]) plugin_objects.add_object( primaryId = handleEmpty(macAddress), @@ -121,7 +155,7 @@ def main(): foreignKey = handleEmpty(macAddress) ) - mylog('verbose', ['[SNMPDSC] Entries found: ', len(plugin_objects)]) + mylog('verbose', [f"[{pluginName}] Entries found: ", len(plugin_objects)]) plugin_objects.write_result_file()