Saturday, December 1, 2007

Ping Multiple Hosts Using Bash Nmap and Fping

I explained how to get a list of hosts using nmap -- using-nmap-to-generate-host-lists.html, but here is another look at the subject.

The question is, how do I ping multiple hosts to find out which ones are down? Sure this could be considered a topic of system monitoring, but maybe you just want to reboot a bunch of machines, and make sure they all come back online. This quick check will tell you whether there is a problem or not.

Here are three methods for pinging a list of hosts:

1.) for host in `cat all.txt `;do ping -c 1 $host; done
2.) nmap -sP -R -iL all.txt
3.) sudo fping -u < xyz/all.txt

First, we assume that you have a text file named all.txt that contains a list of hostnames, one per line. Obviously, the examples here contain fake hostnames, domain names and IPs, as described in another article about vi: vim-tips-search-and-replace.html.

--- all.txt ---
xyz-1
xyz-2
xyz-3
xyz-4
xyz-5
xyz-6
xyz-7
xyz-8
--- end all.txt ---

1.) Use a for loop in the shell

$ for host in `cat all.txt `;do ping -c 1 $host; done
PING xyz-1.example.com (10.10.10.34) 56(84) bytes of data.
64 bytes from xyz-1.example.com (10.10.10.34): icmp_seq=0 ttl=62 time=0.472 ms

--- xyz-1.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.472/0.472/0.472/0.000 ms, pipe 2
PING xyz-2.example.com (10.10.10.35) 56(84) bytes of data.
64 bytes from xyz-2.example.com (10.10.10.35): icmp_s eq=0 ttl=62 time=0.459 ms

--- xyz-2.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.459/0.459/0.459/0.000 ms, pipe 2
PING xyz-3.example.com (10.10.10.36) 56(84) bytes of data .
64 bytes from xyz-3.example.com (10.10.10.36): icmp_seq=0 ttl=62 time=0.390 ms

--- xyz-3.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.390/0.390/0.390/0.000 ms, pipe 2
PING xyz-4.example.com (10.10.10.37) 56(84) bytes of data.
64 bytes from xyz-4.example.com (10.10.10.37): icmp_s eq=0 ttl=62 time=0.382 ms

--- xyz-4.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.382/0.382/0.382/0.000 ms, pipe 2
PING xyz-5.example.com (10.10.10.130) 56(84) bytes of data .
64 bytes from xyz-5.example.com (10.10.10.130): icmp_seq=0 ttl=63 time=0.195 ms

--- xyz-5.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.195/0.195/0.195/0.000 ms, pipe 2
PING xyz-6.example.com (10.10.10.131) 56(84) bytes of data .

--- xyz-6.example.com ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

You can see that even for the six hosts we are checking for, there is quite a bit of extra info to look through to find out if a host is up or not.


$ for host in `cat xyz/all.txt `;do ping -c 1 $host; done | grep -v "\=" | grep -v PING
--- xyz-1.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms

--- xyz-2.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms

--- xyz-3.example.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms

Not as bad, but we can do better. The above example still takes three lines to show the results (hostname, %packet loss, and a blank line). Using a different tool give much better results, so why did I waste your time looking at this method? Well, it still may be easier to use ping, since it will most likely be installed by default, and you can do tricks with the input file that may not work with the other methods. For example, if your input file contains lines that start with a "#" you can use grep -v to skip those lines, but other tools may expect cleaner input.

2.) Use nmap

So let's try the same thing with nmap.

$ nmap -sP -R -iL all.txt
Reading target specifications from FILE: all.txt


Starting nmap V. 3.00 ( www.insecure.org/nmap/ )
Host xyz-1.example.com (10.10.10.34) appears to be up.
Host xyz-2.example.com (10.10.10.35) appears to be up.
Host xyz-3.example.com (10.10.10.36) appears to be up.
Host xyz-4.example.com (10.10.10.37) appears to be up.
Host xyz-5.example.com (10.10.10.130) appears to be up.
Host xyz-6.example.com (10.10.10.131) appears to be down.

Much cleaner, isn't it?

For even less info to sift through, you can use grep to look for hosts that are up or down.

$ nmap -v -sP -R -iL all.txt | grep down
Host xyz-6.example.com (10.10.10.131) appears to be down.

Then, of course it can be cleaned up further with a simple awk print statement.

$ nmap -v -sP -R -iL all.txt | grep down | awk '{print $2}'
xyz-6.example.com

3.) Use fping

Another option is fping, but it has the disadvantage that it is probably not installed by default, and it has to be run as root (or sudo), unless additional configuration is performed. It does give very nice output, and is very fast.

Show me all hosts from my list that are down:
$ sudo fping -u < all.txt
xyz-6


7 comments:

Anonymous said...

Great round up! Love the explanation of using awk here as well, so many things to do on the command line, this is where BASH shines, tying them all together with a for loop!

Tejas Bhalerao said...

Thats what i needed for System monitoring!
Thanks a lot Sir!

scottm said...

I am glad people find this useful.

Alvaro Q. said...

# nmap -sP -R 192.168.1.0/24 | grep up
Host 192.168.1.1 is up.
Host 192.168.1.2 is up (0.00038s latency).
Host 192.168.1.11 is up (0.000089s latency).
Nmap done: 256 IP addresses (3 hosts up) scanned in 5.54 seconds

Asi tambien funciona, Gracias!!

scottm said...

Alvaro Q.

Español: Tiene razón. Se puede usar un subnet, pero a veces quieres limitar la búsqueda a unas cuantas máquinas. Por ejemplo, una red puede tener 10,000 máquinas, y solo quiero saber que pasa con 200. Yo uso un archivo como esta (subnets.net), que puede tener muchas redes (subnets). De ahí saco la lista de máquinas que me interesan, pero también se puede buscar información de todas las máquinas prendías o apagadas.


English:
Alvro Q. is correct. You can simply specify a subnet, but you may want to check just a few hosts. For example, a network may contain 10,000 hosts, but you are interested in only 200. I use a file like this (subnets.net) that may contain many networks. I use that to extract the hosts I am interested in, but there may be times when you want to find all hosts that are up or down.

subnets.net
192.168.1.*
192.168.2.*
192.168.3.*
192.168.4.*
10.1.1.*
10.1.2.*

Thunder Emperor said...

Even when we use the for loop method, a cleaner way will be to ditch the output by redirecting it out to dev null and then checking the $? value and directly ping it

scottm said...

Excellent point by Thunder Emperor. Sometimes it's easier to deal with return values instead of trying to format the output of a program such as ping which may be different across platforms.

Here is one way to use $?. You just have to be aware that 0 = success (host is up), 1 = ping failed (host is down), 2 = unknown host (host is not in DNS). Of course you can program the output as you like, but this example is not bad for a quick on-liner.

$ for host in abc-01 abc-02 abc-03 xyz-01; do ping -c 1 $host > /dev/null 2>&1; echo $host: $?;done

abc-01: 0
abc-02: 0
abc-03: 1
xyz-01: 2
$

Note that you can either redirect stderrm, or use "| grep -v ping" to get rid of the extra line of output for unknown hosts, e.g.:

ping: unknown host xyz-01
xyz-01: 2

This should give the same results:

$ for host in abc-01 abc-02 abc-03 xyz-01; do ping -c 1 $host > /dev/null; echo $host: $?;done| grep -v ping