24

I found scripts that say they check for internet connectivity. Some check the IP address if the interface is up BUT it does not check for internet connectivity. I found some that uses ping like this: if [ 'ping google.com -c 4 | grep time' != "" ]; then but sometimes this may be unreliable as ping itself may hang for some reason (e.g. waiting for some stuck IO).

Any suggestions on the proper/reliable way to check for internet connectivity using scripts? Do I have to use some packages?

It needs to be able to check periodically with cron for example, then, do something when the connection goes down like invoke ifup --force [interface]

26

Testing IPv4 connectivity

If your network lets pings through, try pinging 8.8.8.8 (a server run by Google).

if ping -q -c 1 -W 1 8.8.8.8 >/dev/null; then
  echo "IPv4 is up"
else
  echo "IPv4 is down"
fi

Testing IP connectivity and DNS

If you only want the test to succeed when DNS is also working, use a host name.

if ping -q -c 1 -W 1 google.com >/dev/null; then
  echo "The network is up"
else
  echo "The network is down"
fi

Testing web connectivity

Some firewalls block pings. Some places have a firewall that blocks all traffic except via a web proxy. If you want to test web connectivity, you can make an HTTP request.

case "$(curl -s --max-time 2 -I http://google.com | sed 's/^[^ ]*  *\([0-9]\).*/\1/; 1q')" in
  [23]) echo "HTTP connectivity is up";;
  5) echo "The web proxy won't let us through";;
  *) echo "The network is down or very slow";;
esac
  • You may want to incorporate validation of a physical connection (OSI layer 1) prior to any OSI layer 3 checks using ethtool; $ ethtool <dev> | awk '$0 ~ /link detected/{print $3}' – jas- May 6 '18 at 2:46
  • Can you explain the purpose of adding this please >/dev/null – Amine Harbaoui Aug 24 '18 at 9:09
  • @AmineHarbaoui – >/dev/null redirects standard output to /dev/null, the null device, which disposes of it since it is not desired in this case (all we care about is the exit values of the commands). Instead, more applicable output is taken from the echo lines. – Adam Katz Jan 5 at 16:37
26

I highly recommend against using ping to determine connectivity. There are too many network admins that disable ICMP (the protocol it uses) due to worries about ping flood attacks originating from their networks.

Instead, I use a quick test of a reliable server on a port you can expect to be open:

if nc -zw1 google.com 443; then
  echo "we have connectivity"
fi

This uses netcat (nc) in its port scan mode, a quick poke (-z is zero-I/O mode [used for scanning]) with a quick timeout (-w 1 waits at most one second). It checks Google on port 443 (HTTPS).

I've used HTTPS rather than HTTP as an effort to protect against captive portals and transparent proxies which can answer on port 80 (HTTP) for any host. This is less likely when using port 443 since there would be a certificate mismatch, but it does still happen.

If you want to proof yourself against that, you'll need to validate the security on the connection:

test=google.com
if nc -zw1 $test 443 && echo |openssl s_client -connect $test:443 2>&1 |awk '
  handshake && $1 == "Verification" { if ($2=="OK") exit; exit 1 }
  $1 $2 == "SSLhandshake" { handshake = 1 }'
then
  echo "we have connectivity"
fi

This checks for a connection (rather than waiting for openssl to time out) and then makes the SSL handshake, keying on the verification phase. It silently exits ("true") if the verification was "OK" or else exits with an error ("false"), then we report the finding.

  • 4
    I respect Gillies however this is the right answer. – gwillie Jul 23 '15 at 5:32
  • 3
    add -d e.g. nc -dzw1 also so it doesn't listen for STDIN and hang indefinetily in a script. and maybe use 8.8.8.8 instead of google.com to save a lookup. nc -dzw1 8.8.8.8 443 – dezza Feb 17 '17 at 13:45
  • I'm not sure about how reliable Google's DNS resolver is at serving HTTPS. The google.com server should be more reliable for HTTPS (unless you're in China, but then both are likely blocked). I've never needed -d in my scripts, perhaps because I've never had an unused pipeline. That should be safe to add. – Adam Katz Feb 17 '17 at 17:05
  • 1
    @dezza – -w 1 still costs a second when there is no connectivity, though perhaps your nc has some kind of obscure issue somwehere. If you have a recent version of nmap installed, you can instead do ncat --send-only --recv-only -w 334ms to cut that failure time to a third of nc (I've found that 334ms is a good wait time). – Adam Katz Feb 24 '17 at 19:39
  • 1
    @dezza – I do not know why that is happening to you on both nmap's ncat and netcat (nc) for that system. There might be something odd happening in your network or on that BSD system. Feel free to create a new unix.stackexchange question and get more than just my eyes on that problem. If you do, please link it in comments here and link this thread to your new question. – Adam Katz Feb 27 '17 at 18:27
9

I made a script that uses multiple ways to check internet connection (ping, nc, and curl, thanks to Adam Katz, Gilles, and Archemar). I hope someone finds this useful. Feel free to edit it to your liking/optimize it.

Checks your gateway, DNS, and internet connection (using curl, nc, and ping). Put this in a file then make it executable (Usually sudo chmod +x filename)

#!/bin/bash

GW=`/sbin/ip route | awk '/default/ { print $3 }'`
checkdns=`cat /etc/resolv.conf | awk '/nameserver/ {print $2}' | awk 'NR == 1 {print; exit}'`
checkdomain=google.com

#some functions

function portscan
{
  tput setaf 6; echo "Starting port scan of $checkdomain port 80"; tput sgr0;
  if nc -zw1 $checkdomain  80; then
    tput setaf 2; echo "Port scan good, $checkdomain port 80 available"; tput sgr0;
  else
    echo "Port scan of $checkdomain port 80 failed."
  fi
}

function pingnet
{
  #Google has the most reliable host name. Feel free to change it.
  tput setaf 6; echo "Pinging $checkdomain to check for internet connection." && echo; tput sgr0;
  ping $checkdomain -c 4

  if [ $? -eq 0 ]
    then
      tput setaf 2; echo && echo "$checkdomain pingable. Internet connection is most probably available."&& echo ; tput sgr0;
      #Insert any command you like here
    else
      echo && echo "Could not establish internet connection. Something may be wrong here." >&2
      #Insert any command you like here
#      exit 1
  fi
}

function pingdns
{
  #Grab first DNS server from /etc/resolv.conf
  tput setaf 6; echo "Pinging first DNS server in resolv.conf ($checkdns) to check name resolution" && echo; tput sgr0;
  ping $checkdns -c 4
    if [ $? -eq 0 ]
    then
      tput setaf 6; echo && echo "$checkdns pingable. Proceeding with domain check."; tput sgr0;
      #Insert any command you like here
    else
      echo && echo "Could not establish internet connection to DNS. Something may be wrong here." >&2
      #Insert any command you like here
#     exit 1
  fi
}

function httpreq
{
  tput setaf 6; echo && echo "Checking for HTTP Connectivity"; tput sgr0;
  case "$(curl -s --max-time 2 -I $checkdomain | sed 's/^[^ ]*  *\([0-9]\).*/\1/; 1q')" in
  [23]) tput setaf 2; echo "HTTP connectivity is up"; tput sgr0;;
  5) echo "The web proxy won't let us through";exit 1;;
  *)echo "Something is wrong with HTTP connections. Go check it."; exit 1;;
  esac
#  exit 0
}


#Ping gateway first to verify connectivity with LAN
tput setaf 6; echo "Pinging gateway ($GW) to check for LAN connectivity" && echo; tput sgr0;
if [ "$GW" = "" ]; then
    tput setaf 1;echo "There is no gateway. Probably disconnected..."; tput sgr0;
#    exit 1
fi

ping $GW -c 4

if [ $? -eq 0 ]
then
  tput setaf 6; echo && echo "LAN Gateway pingable. Proceeding with internet connectivity check."; tput sgr0;
  pingdns
  pingnet
  portscan
  httpreq
  exit 0
else
  echo && echo "Something is wrong with LAN (Gateway unreachable)"
  pingdns
  pingnet
  portscan
  httpreq

  #Insert any command you like here
#  exit 1
fi
  • Nice ! Thank you ! What should we set gateway $GW to ? – Ciprian Tomoiagă Jun 8 '17 at 9:45
  • @CiprianTomoiaga No need, /sbin/ip route | awk '/default/ { print $3 }' gets the gateway address from the (hopefully) primary interface. If you want, you may set the gateway IP address yourself. – Gene Dec 4 '17 at 10:28
  • Thanks for this! What I miss though is the option to store internet disruptions in a txt file and an automated email to my ISP. – rhand Oct 2 '18 at 1:57
2

there are many IPs on internet, a light approach is to ping some of them

 if ping -c 4 google.com ; then OK ; else KO ; fi
 if ping -c 4 facebook.com ; then OK ; else KO ; fi
 if ping -c 4 nsa.gov ; then OK ; else KO ; fi # <- this one might not reply

a more complete answer might be getting pages using wget

 wget google.com -o google.txt
 if parse google.txt ; then OK ; else KO ; fi

where

  • parse is a program you write that ensure google.txt is not a (too old) cached version of google.com
1

thanks to your contributions from each user and other web, I managed to complete this script in 3 days. and I will leave it free for its use.

this script automates the renewal of the ip address when connection is lost, it does so persistently.

#!/bin/bash

# Autor: John Llewelyn
# FB: fb.com/johnwilliam.llewelyn
# Twitter: twitter.com/JWLLEWELYN
# TLF: +584-1491-011-15
# Its use is free.
# Description: Connection Monitor for ADSL modem.
# Requirements:
# Copy this code or save to /home/administrator/ConnectionMonitor.sh
# It requires the installed packages fping beep and cron
# Comment the blacklist pcspkr snd-pcsp in /etc/modprobe.d/blacklist.conf
# Give execute permissions: chmod +x /home/administrator/ConnectionMonitor.sh
# Add this line in crontab -e with root user
# @reboot sleep 120 && /home/administrator/MonitorDeConexion.sh

#################################################################################
# SETTINGS
TEST="8.8.8.8"       # TEST PING
ADAPTER1="enp4s0"    # EXTERNAL ETHERNET ADAPTER

# Report
LOGFILE=/home/administrator/Documentos/ReportInternet.log

# Messages
MESSAGE1="Restoring Connectivity..."
MESSAGE2="Wait a moment please..."
MESSAGE3="No Internet connectivity."
MESSAGE4="Yes, there is Internet connectivity."
#################################################################################

# Time and Date
TODAY=$(date "+%r %d-%m-%Y")

# Show IP Public Address
IPv4ExternalAddr1=$(ip addr list $ADAPTER1 |grep "inet " |cut -d' ' -f6|cut -d/ -f1)
IPv6ExternalAddr1=$(ip addr list $ADAPTER1 |grep "inet6 " |cut -d' ' -f6|cut -d/ -f1)

# Alarm
alarm() {
    beep -f 1500 -l 200;beep -f 1550 -l 200;beep -f 1500 -l 200;beep -f 1550 -l 200;beep -f 1500 -l 200;beep -f 1550 -l 200;beep -f 1500 -l 200;beep -f 1550$
}

# Restoring Connectivity
resolve() {
    clear
    echo "$MESSAGE1"
    sudo ifconfig $ADAPTER1 up;sudo dhclient -r $ADAPTER1;sleep 5;sudo dhclient $ADAPTER1
    echo "$MESSAGE2"
    sleep 120
}

# Execution of work
while true; do
    if [[ "$(fping -I $ADAPTER1 $TEST | grep 'unreachable' )" != "" ]]; then
        alarm
        clear
        echo "================================================================================" >> ${LOGFILE}
        echo "$MESSAGE3 - $TODAY"                                                               >> ${LOGFILE}
        echo "$MESSAGE3 - $TODAY"
        echo "================================================================================" >> ${LOGFILE}
        sleep 10
        resolve
    else
        clear
        echo "================================================================================"   >> ${LOGFILE}
        echo "$MESSAGE4 - $TODAY - IPv4 Addr: $IPv4ExternalAddr1 - IPv6 Addr: $IPv6ExternalAddr1" >> ${LOGFILE}
        echo "$MESSAGE4 - $TODAY - IPv4 Addr: $IPv4ExternalAddr1 - IPv6 Addr: $IPv6ExternalAddr1"
        echo "================================================================================"   >> ${LOGFILE}
        sleep 120
    fi
done

pastebin: https://pastebin.com/wfSkpgKA

  • What would make this answer better: (1) Explaining how the script works.  (It looks like the user has to edit the script if his network interface is called anything other than eth0, but this is not mentioned.)  (2) Using English.  (3) Putting all shell variables (e.g., "$HOST", "$LINE1" and "$LOG") into double quotes.  (4) Either set LINE2 or don’t use it.  (I suspect that you got LINE1 / LINE2 confused with inet4 / inet6.) … (Cont’d) – G-Man Dec 4 '17 at 4:02
  • (Cont’d) …  (5) Actually displaying the current time when you say you are displaying the current time, rather than capturing the time when the script starts and displaying it throughout the lifetime of the script.  (6) I think there was something else, but I don’t see it now. – G-Man Dec 4 '17 at 4:02
  • It is in Spanish because it is in my language, but I can correct it in English. The $ HOST is the address to try. $ LINE1 is the Internet connection that is connected by the eth0 adapter. $ LINE2 is the Internet connection that is connected by the eth1 adapter optionally if you have 2 Internet lines, but it is recommended that you leave it disabled. The date, if I verify that it maintains the same time and date since I start the script, I have to correct that problem. This weekend I correct the problem. – John Llewelyn Dec 5 '17 at 21:32
  • Ok G-Man, I made some changes, I still need to correct date and improve some things. – John Llewelyn Jan 5 '18 at 8:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.