Table of Contents

SNMP network scanner

This may be a re-invention of someone else's wheel, but I enjoyed doing it.

It's a bash script which uses nmap and snmpwalk to scan all your locally-connected networks (this should ideally be run on your router / firewall) and (a) finds SNMP-managed switches, and then (b) interrogates them to find out what's plugged in to which port.

It reports:

  1. Which switch ports are directly connected to the router's interfaces
  2. Which switch ports are inter-connected to another switch, if you've cascaded them
  3. Which devices (listed by MAC address, IP address if known, and hostname if available) are connected to the remaining ports

Some ports will show a single device connected, other may show multiple devices; these are connected in between to an unmanaged switch which cannot be interrogated.

The script

SwitchScan.sh
#!/bin/bash

# 1. Scan known networks for SNMP listeners (assumed to be switches)
#
# 2. Run snmpwalk on each switch to find out MAC addresses on each port
#
# 3. Convert MAC addresses to IPs and if possible hostnames
#
# 4. If a MAC address appears more than once in the list, there's a link between switches
# - try to work out what's connected to what
#
# 5. Final outcome - list of MACs, IPs and hostnames plugged in to each switch port

debug=0

tempfile=/tmp/switchscan.$$

required="nmap snmpwalk"
for req in $required
do
  if ! which $req &>/dev/null
  then
    echo "Sorry, but I need $req to be installed first."
    exit 1
  fi
done

# Get a list of my local network connections, scan each in turn
ip route list | grep -v default | cut -d' ' -f1,3 | while read network iface
do
  [ "$debug" -ge "1" ] && echo "Scanning network $network on interface $iface for SNMP devices" >&2
# Find any SMNP managed switches (they'll be listening on UDP port 161)
  nmap -Pn -sU -p 161 $network 2>/dev/null | grep -B4 "161/udp open" | grep ^Nmap | rev | cut -d' ' -f1 | rev | tr -d '()' | while read IP
  do
# Ask each switch what MAC addresses it has on each port
    snmpwalk -v2c -c public $IP .1.3.6.1.2.1.17.4.3.1.2 2>/dev/null | grep ^iso | cut -d'.' -f12- | grep -v ^1.128.194 | grep -v " = INTEGER: 0" | while read SNMPout
# iso.3.6.1.2.1.17.4.3.1.2.56.234.167.167.63.123 = INTEGER: 5
    do
      [ "$debug" -ge "2" ] && echo "SNMP response from $IP was $SNMPout" >&2
# Note the port number and convert each MAC address to hex
      MACdec=${SNMPout% = *}
      port=${SNMPout##* }
      MACh=
      for byte in `echo $MACdec | tr '.' ' '`
      do
        MACh=$MACh:`printf "%02x" $byte`
      done
      MAChex=${MACh#:}
# Look up the MAC address in the arp table to see if we know its IP (and possibly hostname)
      rarp=`arp -a | grep $MAChex | tr -d '()' | cut -d' ' -f1,2,7 | tr -d '?'`
      subnet=${rarp##* }
      nameIP=${rarp% *}
      hostname=${rarp%% *}
      hostIP=${nameIP##* }
      echo "$IP port $port = ${subnet:--} $MAChex $hostIP $hostname"
      [ "$debug" -ge "2" ] && echo "$IP port $port = ${subnet:--} $MAChex $hostIP $hostname" >&2
    done
  done
# Send everything to a temp file so we can process later
done >$tempfile.pass1

# What are our own interfaces' MAC addresses?
ip route list | grep -v default | cut -d' ' -f3 | while read net
do
  mac=`ifconfig $net | grep ether | tr -s ' ' | cut -d' ' -f3`
  if [ -n "$mac" ]
  then
# If we find our own MAC address on a switch port, we're obviously connected (somehow)
    grep $mac $tempfile.pass1 | cut -d ' ' -f1-3 | while read swport
    do
# Make sure we are the only thing on that port, otherwise it's an indirect connection
      [ `grep -c "^$swport " $tempfile.pass1` -eq 1 ] && echo "$swport is connected to my $net interface"
    done
  fi
done

echo

# Get each switch's IP and see if other switches are plugged in to it
cut -d' ' -f1 $tempfile.pass1 | sort -u | while read swIP
do
  grep " $swIP " $tempfile.pass1 | cut -d' ' -f1-3 | while read conSW2
  do
    if [ `grep -c " ${conSW2%% *} " $tempfile.pass1` -eq 1 ]
    then
      conSW1=`grep " ${conSW2%% *} " $tempfile.pass1 | cut -d ' ' -f1-3`
# Output to another temporary file because we want the details later as well
      echo "$conSW1~$conSW2"
    fi
  done
done >$tempfile.pass2

# Print the inter-switch connections to console
cat $tempfile.pass2 | tr '~' ' ' | while read IP1 x1 port1 IP2 x2 port2
do
  echo "$IP1 port $port1 is connected to $IP2 port $port2"
done | sort -nk3

echo

# Look at the original results again but without the interconnected ports
# This should just be the remaining client machines (sometimes more than one per port, due to unmanaged switches in between)

cut -d' ' -f1 $tempfile.pass1 | sort -u | while read swIP
do
  ( cat $tempfile.pass2 | tr '~' '\n'; ifconfig | grep HWaddr | tr -s ' ' | cut -d' ' -f5 ) | grep -vf - $tempfile.pass1 | grep "^$swIP " | while read s x1 port x2 net mac cIP cname
  do
    echo "$swIP port $port is connected to $mac $cIP ${cname:+($cname)}"
  done | sort -nk3
  echo
done

# Tidy up temp files
rm -f $tempfile.*

Sample output

192.168.32.1 port 1 is connected to my clients interface
192.168.36.1 port 1 is connected to my servers interface

192.168.36.2 port 1 is connected to 192.168.36.1 port 24
192.168.32.1 port 23 is connected to 192.168.32.3 port 1
192.168.32.1 port 24 is connected to 192.168.32.2 port 8
192.168.36.1 port 24 is connected to 192.168.36.2 port 1

192.168.32.1 port 3 is connected to 00:25:9c:38:e7:94 192.168.32.16 (WRT54.clients)
192.168.32.1 port 3 is connected to 2c:3a:e8:3b:ac:ca 192.168.32.233 (Sonoff-Two.clients)
192.168.32.1 port 3 is connected to 2c:3a:e8:4e:9c:7e 192.168.32.230 (Sonoff-One.clients)
192.168.32.1 port 3 is connected to 5c:51:81:7c:b7:01 192.168.32.102 (Galaxy-J3.clients)
192.168.32.1 port 3 is connected to b4:e6:2d:14:27:64 192.168.32.220 (Wemos-Three.clients)

192.168.32.2 port 1 is connected to 50:46:5d:b5:9c:58 192.168.32.35 (Pikantus.clients)

192.168.32.3 port 3 is connected to 00:19:99:7f:c4:6a 192.168.32.164 (Fujitsu.clients)
192.168.32.3 port 5 is connected to 52:54:00:1e:dd:f8 192.168.32.58 
192.168.32.3 port 5 is connected to 52:54:00:5b:05:d8 192.168.32.59 (FreeSwitch.clients)
192.168.32.3 port 5 is connected to c8:3a:35:d8:00:10  
192.168.32.3 port 7 is connected to bc:5f:f4:89:a0:94 192.168.32.151 (Stella.clients)

192.168.36.1 port 4 is connected to 00:13:d3:b3:4b:1f 192.168.36.12 
192.168.36.1 port 5 is connected to e8:39:35:2d:f0:cc 192.168.36.22 (Masham.servers)
192.168.36.1 port 23 is connected to 00:c0:02:4a:62:b6 192.168.36.20 (PrintUSB.servers)

192.168.36.2 port 4 is connected to 00:b0:c2:01:91:ed  
192.168.36.2 port 4 is connected to 52:54:00:75:33:45  
192.168.36.2 port 4 is connected to 52:54:00:be:a4:55 192.168.36.27 (Collector.servers)
192.168.36.2 port 4 is connected to 52:54:00:c5:5d:69 192.168.36.24 (ZoneMinder.servers)
192.168.36.2 port 5 is connected to 38:ea:a7:a7:3f:7b 192.168.36.15 (SesteriLO.servers)
192.168.36.2 port 6 is connected to 28:92:4a:32:02:33 192.168.36.11 (Sester.servers)
192.168.36.2 port 7 is connected to 78:ac:c0:f7:88:ae 192.168.36.9 (Sester2.servers)

Further development

I'm sure there's plenty of other interesting information which can be got out of these switches, such as their names, locations, size, speed, etc.

VLANs are another step too.


Go up
Return to main index.