Performing Ping Sweeps with IOS TclSh

It’s been a while since I’ve gotten a blog post up, but with my CCIE recertification out of the way I’m hoping to ramp some volume back up. We’re talking about some sexy stuff today… Ping sweeps! First off, let’s cover why you’d need to sweep up your pings. Some people use the ping sweep as a means to “find” hosts on the network. The problem with this is, devices with host-based firewalls active may not respond to an ICMP ping. If you’re pinging from off the local subnet, there are other reasons you might not get a response back as well, like a host having a mis-programmed default gateway or subnet mask, or an interface ACL on the routing device. That said, ping sweeps are still incredibly useful for helping to find vacant IP addresses on a LAN. Or, at least, IP addresses that are not currently active. Always consult your properly maintained IP documentation to find IPs you can safely use for new deployments (yes, I’m laughing at that one too…).

Anyway, how do ping sweeps help identify active IPs if we can’t trust the ping responses? Well, just because a device may not respond to the ICMP echo request from the ping, the device will respond to an ARP request for its IP address on the subnet. It doesn’t have a choice, it’s a requirement for an IP(v4) stack to operate and can’t be blocked by any host firewall. (At this point I’m sure some reader will identify a situation where ARP replies can also be restricted by some means, but I’ll point out that in working on hundreds of enterprise and data center networks, I’ve never found a host that did not reply to every single ARP request it received.) So my goal of a ping sweep isn’t to get ping responses, it’s to “wake up” the ARP cache of a host or network device on the subnet so that the ARP cache can be inspected to determine what IPs are really alive. This information can then be used to identify candidate IP addresses to assign to something. In my role as a consultant often working remotely on things, the ability to propose IPs for assignment to be confirmed by the customer is usually faster than asking the customer to identify the IPs in the first place.
Some time ago, Greg Ferro posted a simple Bash script for doing a ping sweep which, if executed from on the same broadcast domain as the subnet you’re sweeping, would work just fine to populate the ARP cache of the host. The nmap tool is another great way to scan a connected subnet for live hosts. But what if you’re working remotely on a segment where you only have access to a Cisco networking device? Well, typing “ping 10.1.1.1,” then waiting for the timeouts, and then typing “ping 10.1.1.2” will get old by about the time you get to 10.1.1.4. You’d want the ability to cycle through addresses quickly.
When pinging from a Cisco router or switch, we have another trick we can use to speed things up: if the ping timeout is specified as zero, the router/switch will send an ARP request (if the MAC to IP binding has not already been resolved) followed by the pings, but not wait for any timeout of the ping itself before continuing on. I typically send two such requests to ensure I get a reply.
There is another situation that I’ve had occasion to ping a large range of addresses from the attached layer 3 switch is to refresh client ARP caches when migrating layer 3 gateway services from one device to another. In a perfect world, everything would be configured for a FHRP and/or pick up gratuitous ARP announcements when the gateway services move. In practice, it doesn’t always go down that way, so a trick to force a refresh on client ARP cache entries for the default gateway can really ease the pain of a gateway migration.
So, how do we do this CLI magic? The IOS Tcl Shell can easily be used for variable-based scripts and with some experimentation, control loops can make pinging large IP ranges fairly painless (if a bit time-consuming).
Our first example is the “classic” ping script that every CCIE candidate has memorized:
foreach ip {
4.2.2.2
8.8.8.8
8.8.4.4} {
ping $ip repeat 2 timeout 1 }
This works to confirm reachability to a set of individual, known targets, but to perform a full-subnet ping sweep this way would require a lot of typing or text-editor-fu. Here it is in action. It works, just not very efficiently for our scenario where you need to hit every possible host IP.
Here is the topology I am testing with:
IOSPing
R1#show arp
Protocol  Address          Age (min)  Hardware Addr   Type   Interface
Internet  192.168.14.1            -   ca01.cc3c.0000  ARPA   FastEthernet0/0
Internet  192.168.33.1            -   ca01.cc3c.001d  ARPA   FastEthernet1/1
Internet  192.168.174.1           -   ca01.cc3c.001c  ARPA   FastEthernet1/0

R1#tclsh
R1(tcl)#foreach ip {
+>(tcl)#192.168.14.2
+>(tcl)#192.168.14.3
+>(tcl)#192.168.14.4
+>(tcl)#192.168.14.5
+>(tcl)#192.168.14.53} {
+>(tcl)#ping $ip repeat 2 timeout 1 }
Type escape sequence to abort.
Sending 2, 100-byte ICMP Echos to 192.168.14.2, timeout is 1 seconds:
..
Success rate is 0 percent (0/2)
Type escape sequence to abort.
Sending 2, 100-byte ICMP Echos to 192.168.14.3, timeout is 1 seconds:
..
Success rate is 0 percent (0/2)
Type escape sequence to abort.
Sending 2, 100-byte ICMP Echos to 192.168.14.4, timeout is 1 seconds:
..
Success rate is 0 percent (0/2)
Type escape sequence to abort.
Sending 2, 100-byte ICMP Echos to 192.168.14.5, timeout is 1 seconds:
..
Success rate is 0 percent (0/2)
Type escape sequence to abort.
Sending 2, 100-byte ICMP Echos to 192.168.14.53, timeout is 1 seconds:
.!
Success rate is 50 percent (1/2), round-trip min/avg/max = 32/32/32 ms

R1(tcl)#exit
Protocol  Address          Age (min)  Hardware Addr   Type   Interface
Internet  192.168.14.1            -   ca01.cc3c.0000  ARPA   FastEthernet0/0
Internet  192.168.14.2            0   Incomplete      ARPA
Internet  192.168.14.3            0   Incomplete      ARPA
Internet  192.168.14.4            0   Incomplete      ARPA
Internet  192.168.14.5            0   Incomplete      ARPA
Internet  192.168.14.53           0   0050.7966.6801  ARPA   FastEthernet0/0
Internet  192.168.33.1            -   ca01.cc3c.001d  ARPA   FastEthernet1/1
Internet  192.168.174.1           -   ca01.cc3c.001c  ARPA   FastEthernet1/0
The “Incomplete” entries can be considered, with high confidence, not to be present on the network right now. The answered entries are basically guaranteed to be active on the network. Notice one of the host targets did actually sneak a response in within the “zero” timeout. That happens occasionally. Doesn’t matter.
But! Tcl can use incremented control loops, of course, like a for loop. So with just a couple lines of syntax, we can loop through an entire /24 prefix:
Loop for a whole subnet
for {set i 1} {$i < 255} {incr i} {
ping 10.100.$subnet.$i re 2 ti 0
As ‘i’ is incremented, the ping command is run with the zero timeout. Then ‘i’ is incremented and the process repeats. The result looks like this:
R1#show arp
Protocol  Address          Age (min)  Hardware Addr   Type   Interface
Internet  192.168.14.1            -   ca01.cc3c.0000  ARPA   FastEthernet0/0
Internet  192.168.33.1            -   ca01.cc3c.001d  ARPA   FastEthernet1/1
Internet  192.168.174.1           -   ca01.cc3c.001c  ARPA   FastEthernet1/0
R1#tclsh
R1(tcl)#for {set i 1} {$i < 255} {incr i} { 
>(tcl)#ping 192.168.14.$i re 2 ti 0
>(tcl)#}
Type escape sequence to abort.
Sending 2, 100-byte ICMP Echos to 192.168.14.1, timeout is 0 seconds:
!!
Success rate is 100 percent (2/2), round-trip min/avg/max = 4/6/8 ms
Type escape sequence to abort.
Sending 2, 100-byte ICMP Echos to 192.168.14.2, timeout is 0 seconds:
..
Success rate is 0 percent (0/2)
Type escape sequence to abort.
Sending 2, 100-byte ICMP Echos to 192.168.14.3, timeout is 0 seconds:
..
Success rate is 0 percent (0/2)
Type escape sequence to abort.
Sending 2, 100-byte ICMP Echos to 192.168.14.4, timeout is 0 seconds:
..

(THIS GOES ON FOR A WHILE)

Sending 2, 100-byte ICMP Echos to 192.168.14.254, timeout is 0 seconds:
..
Success rate is 0 percent (0/2)
R1(tcl)#exit
R1#show arp
Protocol  Address          Age (min)  Hardware Addr   Type   Interface
Internet  192.168.14.1            -   ca01.cc3c.0000  ARPA   FastEthernet0/0
Internet  192.168.14.2            0   Incomplete      ARPA
Internet  192.168.14.3            0   Incomplete      ARPA
Internet  192.168.14.4            0   Incomplete      ARPA
Internet  192.168.14.5            0   Incomplete      ARPA
Internet  192.168.14.6            0   Incomplete      ARPA

(AGAIN, A BIG LIST OF INCOMPLETES)

R1#show arp | exclude Incomplete
Protocol  Address          Age (min)  Hardware Addr   Type   Interface
Internet  192.168.14.1            -   ca01.cc3c.0000  ARPA   FastEthernet0/0
Internet  192.168.14.53           0   0050.7966.6801  ARPA   FastEthernet0/0
Internet  192.168.14.69           0   0050.7966.6802  ARPA   FastEthernet0/0
Internet  192.168.33.1            -   ca01.cc3c.001d  ARPA   FastEthernet1/1
Internet  192.168.174.1           -   ca01.cc3c.001c  ARPA   FastEthernet1/0 
This is much faster and more useful. Since it tries every IP in the subnet, it can also be used to “force” an ARP update of the pinging device’s MAC address in the other devices on the network.
Even this can be slow if you have a number of attached subnets, though. But the looping idea can be expanded further with nesting to produce a list of subnets which should be fully scanned. The one assumption we’re making here is that the first two octets are the same and they are all at least /24 prefixes (or, the ranges need to be specified in terms of a complete /24 at a time).
Nested loops for multiple subnets
foreach subnet {
10
11 } {
for {set i 1} {$i < 255} {incr i} {
ping 10.100.$subnet.$i re 2 ti 0
}
}
In action:
foreach subnet {
14
33
174 } {
for {set i 1} {$i < 255} {incr i} {
ping 192.168.$subnet.$i re 2 ti 0
}
}
It took about 5-6 seconds to scan those 3 /24s and we found every active host.
Protocol  Address          Age (min)  Hardware Addr   Type   Interface
Internet  192.168.14.1            -   ca01.cc3c.0000  ARPA   FastEthernet0/0
Internet  192.168.14.53           0   0050.7966.6801  ARPA   FastEthernet0/0
Internet  192.168.14.69           0   0050.7966.6802  ARPA   FastEthernet0/0
Internet  192.168.33.1            -   ca01.cc3c.001d  ARPA   FastEthernet1/1
Internet  192.168.33.9            0   0050.7966.6805  ARPA   FastEthernet1/1
Internet  192.168.33.101          0   0050.7966.6803  ARPA   FastEthernet1/1
Internet  192.168.33.217          0   0050.7966.6804  ARPA   FastEthernet1/1
Internet  192.168.174.1           -   ca01.cc3c.001c  ARPA   FastEthernet1/0
Internet  192.168.174.91          0   0050.7966.6806  ARPA   FastEthernet1/0
Internet  192.168.174.220         0   0050.7966.6807  ARPA   FastEthernet1/0
Now, obviously there are still practical limits here. Trying to scan an attached /16 would probably take a while. Actually, I got curious after writing that last sentence and tried this:
for {set j 1} {$j < 255} {incr j} {
for {set i 1} {$i < 255} {incr i} {
ping 192.168.$j.$i re 2 ti 0
}
}
Scanning 192.168.0.0/16 from the router took about 6 minutes. That’s actually not *so* bad. That still doesn’t make assigning  connected interfaces a /16 mask a good idea!
 A Real World Example
I was recently trying to identify available IPs for a single-to-dual-core conversion where we had to come up with new host IPs for each subnet for the “real” IPs of the two new core switches which would then use the existing gateway IP for the VIP. Since there were a handful of subnets to check for available IPs, and I was hoping to use the same pair of IPs in each subnet for consistency, it was easy to loop through a set of subnets, checking the .253 and .254 hosts:
foreach subnet {
170
180
190
192
200
201
204 } {
for {set i 253} {$i < 255} {incr i} {
ping 192.168.$subnet.$i re 2 ti 0
}
}


SW01#sh arp | i 253|254
Internet 192.168.180.253 0 Incomplete ARPA
Internet 192.168.180.254 0 Incomplete ARPA
Internet 192.168.190.254 0 Incomplete ARPA
Internet 192.168.190.253 0 Incomplete ARPA
Internet 192.168.170.254 0 Incomplete ARPA
Internet 192.168.170.253 0 Incomplete ARPA
Internet 192.168.192.253 0 Incomplete ARPA
Internet 192.168.192.254 0 Incomplete ARPA
Internet 192.168.204.253 0 Incomplete ARPA
Internet 192.168.204.254 0 Incomplete ARPA
Internet 192.168.201.253 0 Incomplete ARPA
Internet 192.168.200.253 0 Incomplete ARPA
Internet 192.168.200.254 0 Incomplete ARPA
Internet 192.168.201.254 0 Incomplete ARPA
So, does this replace good IPAM and documentation? No, but sometimes you don’t have documentation or can’t trust the doc. Using the Tcl Shell for IP scanning can be a quick and easy way to scan a subnet or force an ARP refresh.
Advertisement

3 thoughts on “Performing Ping Sweeps with IOS TclSh

  1. Mark says:

    thanks for the great writeup! I was curious what kind of load this might put on the device you are running it on? Is this something that should really be done during off hours/maintenance window?

    • bobmccouch says:

      When I’ve used this for small scans I’ve never had it generate a problematic CPU load, but I think it’s certainly prudent to test cautiously before using in a heavily loaded environment that could be easily upset by a small CPU spike. Thanks for reading!

  2. Fay says:

    Very nice poste ! I like it 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

@greatwhitetec

Virtualization, Storage, and other techy stuff

The Stupid Engineer

I ask those questions you're too clever to.

Sunay Tripathi's Blog

Pluribus Networks Founder's Blog on OS, Networking, Virtualization, Cloud Computing, Solaris Architecture, etc

Ed Koehler's Blog

Just another WordPress.com weblog

JGS.io

Data networking, stray thoughts, nerdy fun...

Network Heresy

Tales of the network reformation

The Borg Queen

Jottings on the intersection of tech and humanness

Networking From The Trenches

Ramblings about my thoughts, experiences, and ideas.

Networking 40,000

Attaining my CCIE with the help of Warhammer 40k

Network Shenanigans

Making Packets Do Silly Things

It must be the network...

Ramblings of JD (@subnetwork)

Not Another Network Blog

Musings from yet another IT nerd

rsts11 - Robert Novak on system administration

Resource sharing, time sharing, (20)11 and beyond. A retired sysadmin's blog.

%d bloggers like this: