Network connections fail all the time, we’ve all been there. There are so many things that can go wrong, the network adapter driver can fail, the dhcp server can revoke the lease, the wifi router can disappear, the routing may be wrong at some point along the line, the dns server can be overloaded, or the remote host may be down. Those are some of the possibilities, and it can be quite a pain to track down the problem.
But the first thing to do is to figure out exactly what is working and what isn’t. If you know that much then at least you know where to start. My goal here is to create a fairly simple test to examine the status of the network connection, leading up to a working internet connection. One constraint that I have is that I like it to be portable, so that I can carry it around along with my dotfiles. That means I would like it to work in any location just as long as I can get a shell, it should not require any dependencies.
A fully functional network connection looks like this:

What I do is try to detect the parameters of the network step by step, using the regular tools like route, ifconfig. Once I know what the hosts are, I do a ping. Now, a ping obviously isn’t a foolproof test; if you’re on a network that doesn’t allow outgoing icmp then it’s entirely possible that you can tcp out anyway. So what you really should do is tcp on port 80, not ping. But ping is extremely portable, whereas doing a tcp/udp probe is asking a lot more from the environment, needing something like nmap or hping.
Once you’ve established that the connection is working, and you want to know more about the network, you can go further with something like netscan.
The code is relatively stupid and messy, but that’s the way bash is.
# Author: Martin Matusiak <numerodix@gmail.com> # Licensed under the GNU Public License, version 3 function havenet { local init="$(toolsinit)" platforminit case "$PLATFORM" in "Linux") echo "Platform: $PLATFORM" run_havenet "$init";; *) echo "Platform: $PLATFORM (local network detection not supported)" run_haveinet "$init";; esac } function haveinet { local init="$(toolsinit)" platforminit echo "Platform: $PLATFORM" run_haveinet "$init" } function platforminit { if $(which uname &>/dev/null); then PLATFORM=$(uname 2>/dev/null) fi } function toolsinit { local tools="true" local missing=() local cmds=(/sbin/route /sbin/ifconfig ping nmap) local args=(" -n" "" " -c1 -W2" " -T3") local names=(route ifconfig ping nmap) local i=-1 while [ $i -lt $(( ${#names[@]} - 1 )) ]; do i=$(( $i+1 )) if [ 0 -eq $(which ${cmds[$i]} &>/dev/null ; echo $?) ]; then tools="$tools;local ${names[$i]}=\\"${cmds[$i]}${args[$i]}\\"" else missing[${#missing[@]}]="${cmds[$i]}" fi done if [ ${#missing[@]} -gt 0 ]; then echo -e "Missing tools: ${cred}${missing[*]}${creset}" 1>&2 fi echo "$tools" } function run_havenet { eval "$@" local localranges="169.254 10 172.(1[6-9]|2[0-9]|3[0-1]) 192.168" ### Scan networks echo -e "${cyellow} + Scanning for networks...${creset}" test=$($route 2>/dev/null | egrep "^[1-9]") if [[ $? != 0 ]]; then echo -e " ${cred}none found${creset}" else local nets=$(echo "$test" | sort | awk '{ print $1 }') for net in $nets; do local gw=$($route 2>/dev/null | egrep "^$net" | awk '{ print $3 }') printf " ${cgreen}%-15s${creset} ${ccyan}%s${creset}\\n" "$net" "/ $gw" done ### Detect ips local ips= for net in $nets; do local r=$(echo $net | sed "s/.0$//g" | sed "s/.0$//g" | sed "s/.0$//g") local ip=$($ifconfig 2>/dev/null | grep "inet addr:$r" | sed "s/inet addr:\\([0-9.]*\\).*$/\\1/g") ips="$ip $ips" done echo -e "${cyellow} + Detecting ips...${creset}" test=$(echo "$ips" | egrep -v "^[ ]+$") if [[ $? != 0 ]]; then echo -e " ${cred}none found${creset}" else local on_lan=1 for ip in $ips; do printf " ${cgreen}%-15s${creset} %s" "$ip" "ping: " test=$($ping $ip 2>/dev/null) if [[ $? != 0 ]]; then echo -e "${cred}failed${creset}" else local t=$(echo "$test" | grep "min/avg" | sed "s/.*= \\([0-9.]*\\)\\/.*$/\\1/g") echo -e "${cgreen}$t ms${creset}" fi ### Test ips for lan local ip_on_lan=0 for prefix in $localranges; do test=$(echo "$ip" | egrep "^$prefix" 2>/dev/null) if [[ $? == 0 ]]; then ip_on_lan=1 fi done on_lan=$(( $on_lan & $ip_on_lan )) done if [ $on_lan -eq 1 ]; then ### Detect gateways if on lan echo -e "${cyellow} + Detecting gateways (network is local)...${creset}" test=$($route 2>/dev/null | grep UG) if [[ $? != 0 ]]; then echo -e " ${cred}none found${creset}" else local gws=$(echo "$test" | awk '{ print $2 }') for gw in $gws; do printf " ${cgreen}%-15s${creset} %s" "$gw" "ping: " test=$($ping $gw 2>/dev/null) if [[ $? != 0 ]]; then echo -e "${cred}failed${creset}" else local t=$(echo "$test" | grep "min/avg" | sed "s/.*= \\([0-9.]*\\)\\/.*$/\\1/g") echo -e "${cgreen}$t ms${creset}" fi done ### Try inet connection if we have a gateway run_haveinet "$@" fi else ### Try inet connection, we're on wan run_haveinet "$@" fi fi fi } function run_haveinet { eval "$@" local rootname="A.ROOT-SERVERS.NET." local rootip="198.41.0.4" local dnsport="53" local inethosts="yahoo.com google.com" local inetport="80" ### Test inet connection echo -e "${cyellow} + Testing internet connection...${creset}" echo -en " ${ccyan}$rootname ${cgreen}$rootip${creset} ping: " test=$($ping $rootip 2>/dev/null) if [[ $? != 0 ]]; then echo -e "${cred}failed${creset}" else local t=$(echo "$test" | grep "min/avg" | sed "s/.*= \\([0-9.]*\\)\\/.*$/\\1/g") echo -e "${cgreen}$t ms${creset}" fi ### Detect dns echo -e "${cyellow} + Detecting dns servers...${creset}" test=$(cat /etc/resolv.conf 2>/dev/null | grep ^nameserver) if [[ $? != 0 ]]; then echo -e " ${cred}none found${creset}" else local dnss=$(echo "$test" | awk '{ print $2 }') for dns in $dnss; do printf " ${cgreen}%-15s${creset} %s" "$dns" "ping: " test=$($ping $dns 2>/dev/null) if [[ $? != 0 ]]; then echo -en "${cred}failed${creset}" else local t=$(echo "$test" | grep "min/avg" | sed "s/.*= \\([0-9.]*\\)\\/.*$/\\1/g") echo -en "${cgreen}$t ms${creset}" fi local proto="tcp" ; local udpflags="" if [[ `whoami` = "root" ]]; then proto="udp"; udpflags="-sU -PN" fi echo -en " dns/$proto: " test=$($nmap $dns $udpflags -p $dnsport 2>/dev/null) if ! echo "$test" | grep "$dnsport/$proto" | grep "open" &>/dev/null; then echo -e "${cred}failed${creset}" else local t=$(echo "$test" | grep "scanned in" | sed "s/^.*in \\([0-9.]*\\) seconds.*$/\\1/g") t=$(echo $t*1000 | bc) echo -e "${cgreen}$t ms${creset}" fi done fi ### Test inet dns echo -e "${cyellow} + Testing internet dns...${creset}" for inethost in $inethosts; do printf " ${cgreen}%-15s${creset} %s" "$inethost" "ping: " test=$($ping $inethost 2>/dev/null) if [[ $? != 0 ]]; then echo -en "${cred}failed${creset}" else local t=$(echo "$test" | grep "min/avg" | sed "s/.*= \\([0-9.]*\\)\\/.*$/\\1/g") echo -en "${cgreen}$t ms${creset}" fi echo -en " http: " test=$($nmap $inethost -p $inetport 2>/dev/null) if ! echo "$test" | grep "$inetport/tcp" | grep "open" &>/dev/null; then echo -e "${cred}failed${creset}" else local t=$(echo "$test" | grep "scanned in" | sed "s/^.*in \\([0-9.]*\\) seconds.*$/\\1/g") t=$(echo $t*1000 | bc) echo -e "${cgreen}$t ms${creset}" fi done }
Download this code: networktest.sh
UPDATE: Replacing with newer version that is a bit more clever.
UPDATE2: Added tool detection and platform detection.

Neat. The only problem I had was some hashed out nameservers were reported as not working – the reason they are currently hashed out.
I guess you can change “grep nameserver” to “grep ^nameserver” to fix that.
[...] a follow up to the network perimeter test I have expanded the code a bit. It now shows also the interface names to help explain what’s [...]