Wednesday, March 4, 2009

Episode #6 -- Command-Line Ping Sweeper

Ed Says:

Here's a Windows command to do ping sweeps at the command line:

C:\> FOR /L %i in (1,1,255) do @ping -n 1 10.10.10.%i | find "Reply"

Here, I've got a FOR /L loop, which is a counter. My iterator variable is %i. It starts at 1, steps up by 1 through each iteration through the loop, going up to 255. I want to ping through a /24-sized subnet. I then turn off command echo (@), and ping each IP address once (-n 1). I scrape through the output using the find command, looking for "Reply". The find command is case sensitive, so I put in the cap-R in "Reply". Or, you could use /i to make the find case insensitive.

By the way, you can speed it up by adding "-w 100" to have a timeout of 100 milliseconds between each ping, rather than the normal.

(Note... I had "-t 100" here earlier, but fixed it for "-w 100". Thanks to @bolbroe for the catch. The fact is, I so often use -t with Windows ping to make it keep pinging a la Linux, it feels very natural to put -t in. But, the issue here is to make it wait, with -w, for 100 milliseconds.)

Hal Comments:

I have to admit that my first impulse here was to respond with "sudo apt-get install nmap". But Ed's going to be a stickler for our "built-in shell commands only" rule, so I guess I have to come up with something else.

Here's a fun approach that's very different from Ed's loop:
# ping -b -c 3 >/dev/null 2>&1; arp -an | awk '{print $2}'

ping the broadcast address a few times and then scrape out your ARP table to get the IP addresses of the responding hosts (the old "ARP shotgun" approach). The only problem is that this only works for hosts on your local LAN.

So I guess my final solution is a lot like Ed's:
$ for i in `seq 1 255`; do ping -c 1 10.10.10.$i | tr \\n ' ' | awk '/1 received/ {print $2}'; done

By the way, notice the "tr \\n ' '" hiding in the middle of that shell pipeline? The problem is that the ping command generally produces multi-line output and I need to confirm that the packet was received (last line of output) before printing the IP address I pinged (first line of output). So I'm using tr to convert the multi-line output into a single line that's easier to tokenize with awk. This is a useful little shell programming idiom for your toolkit.

Ed stirs the pot a little bit more:

I like your broadcast ping approach. Nifty! Unfortunately, modern Windows boxen don't respond to broadcast pings. Thus, your command will find Linux and other machines on your same subnet, but not the Windows boxes. I tested it in my lab, and found all my Linux machines happily telling me about their existence, but my super stealthified (NOT!) Windows boxes were silent. Thus, while the broadcast ping is a nifty alternative for some special edge cases (targets on same subnet, don't care to find Windows boxes), I think the sweeper is the better way to go.