40

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]

10 Answers 10

52

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, though Apple OS X users may need to use -G 1 instead). 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 '
  $1 == "SSL" && $2 == "handshake" { handshake = 1 }
  handshake && $1 == "Verification:" { ok = $2; exit }
  END { exit ok != "OK" }'
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.

The awk code analyzes the output of openssl line by line:

  1. If the first word of the line is "SSL" and the second is "Verification", set handshake to 1
  2. If handshake is set and the first word of the line is "Verification",
    then save the second word (the verification status) in ok and stop reading
  3. Exit with a value of 0 (true) if the verification status was OK, or else exit with 1 (false).
    We use != here because shell exit codes are reversed

(An awk oddity: Running exit while reading lines will simply stop reading lines and enter the END condition, from which you can truly exit.)

23
  • 7
    I respect Gillies however this is the right answer.
    – gwillie
    Jul 23, 2015 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
    – dza
    Feb 17, 2017 at 13:45
  • 1
    I discovered that on a school network this command was really slow .. It actually halted my script even though it had the -w 1 timer I couldn't figure out what was going on but I guess it has something to do with network configuration. Odd however. If someone has an idea please reply. I tried port 80 also.
    – dza
    Feb 23, 2017 at 21:59
  • 2
    @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, 2017 at 19:39
  • 3
    @dezza a bit late... but fyi the timeout "not working/the script freezing" appears to be a bug in nc. See this link stackoverflow.com/a/26872879/957573 The timeout didn't work for me either in macOS 10.13.4, and i had to install "gnu netcat 0.7.1" with Homebrew, to work around this.
    – Motsel
    Apr 28, 2018 at 20:58
35

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
4
  • 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, 2018 at 2:46
  • Can you explain the purpose of adding this please >/dev/null Aug 24, 2018 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, 2019 at 16:37
  • Testing IP connectivity (not legacy IPv4!) : testdomain="2001:148f:fffe::1" ## CZ.NIC DNS; and then do test for: nc -zw2 $testdomain 443 >> /dev/null 2>&1 ; ## z w/o traffic, w short wait / attempt. 443 for https ( general availability!) quite unreadable here w/o format but still works ;-) Mar 30 at 0:33
11

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
3
  • Nice ! Thank you ! What should we set gateway $GW to ? Jun 8, 2017 at 9:45
  • 1
    @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.
    – Aloha
    Dec 4, 2017 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, 2018 at 1:57
4

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
2

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

4
  • 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) Dec 4, 2017 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. Dec 4, 2017 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. Dec 5, 2017 at 21:32
  • Ok G-Man, I made some changes, I still need to correct date and improve some things. Jan 5, 2018 at 8:21
2

I am using the following script:

#!/bin/bash

_beep() {
  ( \speaker-test --frequency 400 --test sine -l 1 -p 1 -P 2 >& /dev/null )& # pink / wav
  pid=$!
  \sleep 0.5s
  \kill -9 $pid 
  \wait $pid 2>/dev/null
}

con=0
while [ true ]; do
if ! ping -c1 -W 2 google.com >& /dev/null; then
    echo "No internet..."
    con=0
    _beep
else
    if [[ $con -ne 1 ]]
    then
        echo "Connected."
        con=1
    fi
    sleep 1
fi

done

Here, I am declaring a function beep that is getting called when the ping to the Google server is failing.

1
  • An audible signal is convenient. Try timeout 0.5s speaker-test ... to simplify cutting the playback. For an SSH-friendly alternative, emit the audible bell character: printf '\a'. There's also ping -a, but it is audible on ping success.
    – Joel Purra
    May 14, 2023 at 16:57
1

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

Here's the bash code I use to check Internet connectivity:

while :
do
    socat /dev/null TCP4:google.com:443 && break
    sleep 60
done

This code puts socat in an infinite loop. It repeats until socat returns a $? value of 0, at which point, the break is executed, and the infinite loop is exited for continuation of the script.

This works on my network as I have nothing cached on the WAN side of my firewall. If you know a server more reliable than google.com:443 - you should use that. The /dev/null is included in the socat invocation b/c socat requires two points for a connection. I've found this an effective solution for getting past short-term outages.

0

@PNDA suggested getting data from ethtool, which I like. But, I prefer piping to grep and using a more simple awk command that non-bash people can figure out faster. The time difference between the two is negligible.

Using: Ubuntu Bionic 18.04

Network interface discovery:

root@srv:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:1e:67:96:a3:97 brd ff:ff:ff:ff:ff:ff
    inet 10.0.1.101/8 brd 10.255.255.255 scope global eno1
       valid_lft forever preferred_lft forever
    inet6 fe80::21e:67ff:fe96:a397/64 scope link 
       valid_lft forever preferred_lft forever
3: rename3: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
    link/ether 00:1e:67:96:a3:96 brd ff:ff:ff:ff:ff:ff

ethtool output:

root@srv:~# ethtool eno1
Settings for eno1:
        Supported ports: [ TP ]
        Supported link modes:   10baseT/Half 10baseT/Full 
                                100baseT/Half 100baseT/Full 
                                1000baseT/Full 
        Supported pause frame use: No
        Supports auto-negotiation: Yes
        Supported FEC modes: Not reported
        Advertised link modes:  10baseT/Half 10baseT/Full 
                                100baseT/Half 100baseT/Full 
                                1000baseT/Full 
        Advertised pause frame use: No
        Advertised auto-negotiation: Yes
        Advertised FEC modes: Not reported
        Speed: 1000Mb/s
        Duplex: Full
        Port: Twisted Pair
        PHYAD: 1
        Transceiver: internal
        Auto-negotiation: on
        MDI-X: on (auto)
        Supports Wake-on: pumbg
        Wake-on: g
        Current message level: 0x00000007 (7)
                               drv probe link
        Link detected: yes

Command example:

ethtool eno1 | grep "ink detected" | awk '{print $3}'

Choose your own adventure:

ethtool {{network adapter}} | grep "ink detected" | awk '{print $3}'

Output:

If there is a link:

root@srv:~# ethtool eno1 | grep "ink detected" | awk '{print $3}'
yes

If there is not a link:

root@srv:~# ethtool rename3 | grep "ink detected" | awk '{print $3}'
no
0

If you want to avoid false positives due to the presence of a Captive Portal you'll have to check the output of an HTTP connection against a website of predictable content/answer.

Something like this:

#!/bin/sh

detection_out=$(wget -q http://detectportal.firefox.com/success.txt --timeout=10 -O - 2> /dev/null)

test "$detection_out" = "success"

exit $?

This checks if the content of http://detectportal.firefox.com/success.txt is exactly identical to success.

The output is provided as an exit code: if the exit code is zero, the connection is available, otherwise it is either not available for some reason or blocked by a captive portal.

I took this script from here: https://github.com/libremesh/lime-packages/pull/712/files

0

If you need for you script or alias oneline command to check Internet connectivity on Linux or macOS, you can use this:

[ $(wget --quiet --spider https://google.com &>/dev/null; echo $?) -ne 0 ] && echo "No Internet" && exit

adapted from: 9 commands to check if connected to internet with shell script examples | GoLinuxCloud

1
  • Your [ $(wget --quiet --spider https://google.com &>/dev/null; echo $?) -ne 0 ] is better written just as ! wget --quiet --spider https://google.com &>/dev/null Apr 17, 2023 at 12:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .