Why Does 'Ping' Not Timeout in Linux

Why Ping timeout is not working correctly?

UPDATE: The Why: The most probable reason for the Ping timeout not working, as stated by others as well, is DNS resolution. The system call to getaddrinfo (the one used by Dns.GetHostAddresses and Ping - https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo, https://learn.microsoft.com/en-us/dotnet/api/system.net.dns.gethostaddresses?view=netcore-3.1 - similar for Full Framework) does not accept timeouts. As such, a further improvement to the code below would be to separate the dns lookup from the pinging. Do the lookup first with a timeout approach similar to the code below, and only ping IPs rather than host names, with a specified timeout.

I've run into similar issues in the past, and I have some code that might help in working around this issue. I am editing it here, so it might be less than 100% correct as is, and a bit more complicated than your needs. Can you try something like this?

The hammer: (full code with test results also included below)

private static PingReply ForcePingTimeoutWithThreads(string hostname, int timeout)
{
PingReply reply = null;
var a = new Thread(() => reply = normalPing(hostname, timeout));
a.Start();
a.Join(timeout); //or a.Abort() after a timeout, but you have to take care of a ThreadAbortException in that case... brrr I like to think that the ping might go on and be successful in life with .Join :)
return reply;
}

private static PingReply normalPing(string hostname, int timeout)
{
try
{
return new Ping().Send(hostname, timeout);
}
catch //never do this kids, this is just a demo of a concept! Always log exceptions!
{
return null; //or this, in such a low level method 99 cases out of 100, just let the exception bubble up
}
}

Here is a full working sample (Tasks.WhenAny tested and working in version 4.5.2). I also learned that the elegance of Tasks comes at a more significant performance hit than I remember, but Thread.Join/Abort are too brutal for most production environments.

using System;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
//this can easily be async Task<PingReply> or even made generic (original version was), but I wanted to be able to test all versions with the same code
private static PingReply PingOrTimeout(string hostname, int timeOut)
{
PingReply result = null;
var cancellationTokenSource = new CancellationTokenSource();
var timeoutTask = Task.Delay(timeOut, cancellationTokenSource.Token);

var actionTask = Task.Factory.StartNew(() =>
{
result = normalPing(hostname, timeOut);
}, cancellationTokenSource.Token);

Task.WhenAny(actionTask, timeoutTask).ContinueWith(t =>
{
cancellationTokenSource.Cancel();
}).Wait(); //if async, remove the .Wait() and await instead!

return result;
}

private static PingReply normalPing(string hostname, int timeout)
{
try
{
return new Ping().Send(hostname, timeout);
}
catch //never do this kids, this is just a demo of a concept! Always log exceptions!
{
return null; //or this, in such a low level method 99 cases out of 100, just let the exception bubble up
}
}

private static PingReply ForcePingTimeoutWithThreads(string hostname, int timeout)
{
PingReply reply = null;
var a = new Thread(() => reply = normalPing(hostname, timeout));
a.Start();
a.Join(timeout); //or a.Abort() after a timeout... brrr I like to think that the ping might go on and be successful in life with .Join :)
return reply;
}

static byte[] b = new byte[32];
static PingOptions po = new PingOptions(64, true);
static PingReply JimiPing(string hostname, int timeout)
{
try
{
return new Ping().Send(hostname, timeout, b, po);
}
catch //never do this kids, this is just a demo of a concept! Always log exceptions!
{
return null; //or this, in such a low level method 99 cases out of 100, just let the exception bubble up
}
}

static void RunTests(Func<string, int, PingReply> timeOutPinger)
{
var stopWatch = Stopwatch.StartNew();
var expectedFail = timeOutPinger("bogusdjfkhkjh", 200);
Console.WriteLine($"{stopWatch.Elapsed.TotalMilliseconds} false={expectedFail != null}");
stopWatch = Stopwatch.StartNew();
var expectedSuccess = timeOutPinger("127.0.0.1", 200);
Console.WriteLine($"{stopWatch.Elapsed.TotalMilliseconds} true={expectedSuccess != null && expectedSuccess.Status == IPStatus.Success}");
}

static void Main(string[] args)
{
RunTests(normalPing);
RunTests(PingOrTimeout);
RunTests(ForcePingTimeoutWithThreads);
RunTests(JimiPing);

Console.ReadKey(false);
}
}
}

Some results from my testing:

>Running ping timeout tests timeout = 200. method=normal
>
> - host: bogusdjfkhkjh elapsed: 2366,9714 expected: false=False
> - host: 127.0.0.1 elapsed: 4,7249 expected: true=True
>
>Running ping timeout tests timeout = 200. method:ttl+donotfragment (Jimi)
>
> - host: bogusdjfkhkjh elapsed: 2310,836 expected: false actual: False
> - host: 127.0.0.1 elapsed: 0,7838 expected: true actual: True
>
>Running ping timeout tests timeout = 200. method:tasks
>
> - host: bogusdjfkhkjh elapsed: 234,1491 expected: false actual: False
> - host: 127.0.0.1 elapsed: 3,2829 expected: true=True
>
>Running ping timeout tests timeout = 200. method:threads
>
> - host: bogusdjfkhkjh elapsed: 200,5357 expected: false actual:False
> - host: 127.0.0.1 elapsed: 5,5956 expected: true actual: True

Caution For the Tasks version, even if the calling thread is "unblocked", the action itself, in this case the ping, might linger until it actually times out. That is why I suggest putting in a timeout for the ping command itself as well.

UPDATE Researching the why too, but thought a workaround would help you for now.

New findings:

  • https://stackoverflow.com/a/34238797/8695782
  • https://stackoverflow.com/questions/43260080/ping-timeout-is-unpredictable

PHP Ping on Linux timeout not working

I never could get that to work, instead I used fsockopen. It works great and its much quicker.

function ping_node($ip){
$socket = @fsockopen($ip, "102", $errorNo, $errorStr, 3);
if(!$socket) return "fail";
else return "ok";
}

How to ping in linux until host is known with X seconds timeout?

You need -W option that keeps ping waiting for a timeout of X seconds.

For Linux (iputils):

$ ping -c1 -W10 <url>

For MacOS X:

$ ping -c1 -t10 <url>

Not a one-liner, but this would wait for 10 seconds before timing out:

if ping -c 1 -W 10 www.google.com 1>/dev/null; then 
echo Success;
else
echo Failed;
fi

SendPingAsync Timeouts on WSL

It turns out this was in fact being flood banned, but not by the server. My own router was detecting my pings as a PING OF DEATH ATTACK and blocking them. After messing with wireshark a bit, it looks like the specific thing that is causing my router to detect .NET's SendPingAsync in Linux as an attack, but not rapid pings from either a Windows setup or the ping cli tool in Linux is due to differences in using the Identifier and Sequence Number parts of the ICMP header.

IdentifierSequence Number
Linux ping CLIConstantIncrementing
Windows ICMP system callConstantIncrementing
.NET SendPingAsync in LinuxIncrementingConstant

ping + how to minimize the time of the ping command

The best idea is to run ping in parallel
and then save the result in a file.
In this case your script will run not longer than a second.

for ip in `< list`
do
( ping -c1 $ip || echo ip >> not-reachable ) &
done

Update. In Solaris -c has other meaning, so for solaris you need
run ping other way:

ping $ip 57 1

(Here, 57 is the size of the packet and 1 is the number of the packets to be sent).

Ping's syntax in Solaris:

/usr/sbin/ping -s [-l | -U] [-adlLnrRv] [-A addr_family]
[-c traffic_class] [-g gateway [ -g gateway...]]
[-F flow_label] [-I interval] [-i interface] [-P tos]
[-p port] [-t ttl] host [data_size] [npackets]

You can make a function that aggregates the two methods:

myping()
{
[ `uname` = Linux ] && ping -c 1 "$i" || ping "$ip" 57 1
}
for ip in `< list`
do
( myping $ip || echo ip >> not-reachable ) &
done

Another option, don't use ping directly but use ICMP module from some language.
You can use for example Perl + Net::Ping module from Perl:

perl -e 'use Net::Ping; $timeout=0.5; $p=Net::Ping->new("icmp", $timeout) or die bye ; print "$host is alive \n" if $p->ping($host); $p->close;'

The minimum value for ping -w timeout in practice

This is a fairly well known issue with PING on a windows System.

When running PING with only 1 iteration it can behave unexpectedly, and even ping at LESS than 1 second on the 1st timeout, when the timeout is set to 1 second, which is more strange than the delay added, but not that uncommon (see below I captured one such example it he wild.).

The default timeout is also not stated clearly, but is 3 seconds.

You can test ping and see it behaving wildly with 1 ping set at any value less than 2 seconds, but really the matter seems to be that it can fluxuate by about 500 MS above the value expected due to overhead in instantiating the ping command and calling the TCP/IP stack etc.

I have worked with systems I set up with 10g NICs on a port agg to a nexus switch offloading most of the TCP/IP work to the physical card where the value specified to ping didn't have almost any issue with the timeout.

However I have found that wrapping the PING command inside of a For /F loop will actually reduce the time needed to execute the pings noticeably making it more likely to be.

The only Other alternative is to spawn many parallel command windows, echo the results to a file and then read it, which is still faster.

You can test on the CLI by running this:

echo=%time%&ping -n 1 10.180.7.10>NUL&CALL echo=^%time^%

Note that Ping behaves much more consistently at -n 2 and greater, or with 2000 MS timout or greater.

There the default will be 1 second between timeouts with a fluxulation of x milliseconds, usually the fluxation is much smaller about 100 - 200 ms.


here are some examples from a VM I have:

Cherry Picked:

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 10 10.180.7.10 >NUL&echo=!time!
0:19:35.16
0:19:35.45
(300 MS per ping)

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 1000 10.180.7.10 >NUL&echo=!time!
0:21:49.29
0:21:49.95
(660 MS per ping)

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 1000 10.180.7.10 >NUL&echo=!time!
0:22:41.45
0:22:42.45
(1000 MS per Ping)

C:\Windows\system32>ECHO=!TIME!&Ping -n 2 -l 1 -4 -w 1 10.180.7.10 >NUL&echo=!time!
0:19:57.54
0:19:59.45
(1910 MS - AKA: 955 MS per Ping)

C:\Windows\system32>ECHO=!TIME!&Ping -n 10 -l 1 -4 -w 10 10.180.7.10 >NUL&echo=!time!
0:18:33.11
0:18:46.96
(13850 MS - AKA: 1385 MS per Ping)

C:\Windows\system32>ECHO=!TIME!&Ping -n 10 -l 1 -4 -w 1 10.180.7.1 >NUL&echo=!time!
0:20:33.93
0:20:42.99
(9060 MS - AKA: 906 MS per ping)

Larger Set:

C:\Windows\system32>cmd /v
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.

C:\Windows\system32>ECHO=!TIME!&Ping -n 10 -l 1 -4 -w 10 10.180.7.10 >NUL&echo=!time!
0:18:33.11
0:18:46.96

C:\Windows\system32>ECHO=!TIME!&Ping -n 10 -l 1 -4 -w 10 10.180.7.10 >NUL&echo=!time!
0:19:12.39
0:19:25.95

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 10 10.180.7.10 >NUL&echo=!time!
0:19:35.16
0:19:35.45

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 1 10.180.7.10 >NUL&echo=!time!
0:19:43.61
0:19:43.95

C:\Windows\system32>ECHO=!TIME!&Ping -n 2 -l 1 -4 -w 1 10.180.7.10 >NUL&echo=!time!
0:19:57.54
0:19:59.45

C:\Windows\system32>ECHO=!TIME!&Ping -n 2 -l 1 -4 -w 1 10.180.7.1 >NUL&echo=!time!
0:20:14.24
0:20:15.29

C:\Windows\system32>ECHO=!TIME!&Ping -n 10 -l 1 -4 -w 1 10.180.7.1 >NUL&echo=!time!
0:20:33.93
0:20:42.99

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 1 10.180.7.1 >NUL&echo=!time!
0:20:57.54
0:20:57.60

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 1 10.180.7.10 >NUL&echo=!time!
0:21:08.76
0:21:08.95

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 100 10.180.7.10 >NUL&echo=!time!
0:21:43.01
0:21:43.45

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 1000 10.180.7.10 >NUL&echo=!time!
0:21:49.29
0:21:49.95

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 2000 10.180.7.10 >NUL&echo=!time!
0:22:22.54
0:22:24.45

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 2000 10.180.7.10 >NUL&echo=!time!
0:22:34.04
0:22:35.95

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 1000 10.180.7.10 >NUL&echo=!time!
0:22:41.45
0:22:42.45

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 500 10.180.7.10 >NUL&echo=!time!
0:22:53.92
0:22:54.45

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 100 10.180.7.10 >NUL&echo=!time!
0:23:01.53
0:23:01.95

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 100 10.180.7.10 >NUL&echo=!time!
0:23:05.81
0:23:05.95

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 100 10.180.7.10 >NUL&echo=!time!
0:23:08.98
0:23:09.45

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 100 10.180.7.10 >NUL&echo=!time!
0:23:12.93
0:23:13.45

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 100 10.180.7.10 >NUL&echo=!time!
0:23:19.37
0:23:19.45

C:\Windows\system32>ECHO=!TIME!&Ping -n 1 -l 1 -4 -w 2300 10.180.7.10 >NUL&echo=!time!
0:43:17.04
0:43:18.95



A Faster alternatives

One faster alternative is to Wrap Ping in a For /F loop



@ECHO OFF
FOR /f "tokens=2 delims=:" %%a in ('
ipconfig^|find "IPv4"
') do (
FOR /f "tokens=1-3 delims=. " %%A in ("%%a") do (
ECHO="%%a"_"%%b" ECHO="%%A"_"%%B"_"%%C"
FOR /l %%L in (0,1,255) DO (
FOR /F "Tokens=*" %%l IN ('
ping -n 1 -w 100 %%A.%%B.%%C.%%L
^| FIND /I /V "Pinging "
^| FIND /I /V "Ping Statistics FOR"
^| FIND /I /V "Packets: "
^| FIND /I /V "Approximate round trip "
^| FIND /I /V "Minimum = "
') DO (
CALL ECHO. ^%%TIME^%% - %%A.%%B.%%C.%%L -- %%l
)
)
)
)


Another Faster Alternative method to ping a large set of addresses is to parallelize the pinging and output to a file, and then type the contents



@ECHO OFF
FOR /f "tokens=2 delims=:" %%a in ('
ipconfig^|find "IPv4"
') do (
FOR /f "tokens=1-3 delims=. " %%A in ("%%a") do (
ECHO="%%a"_"%%b" ECHO="%%A"_"%%B"_"%%C"
FOR /l %%L in (0,1,255) DO (
START CMD /C "ping -n 1 -w 100 %%A.%%B.%%C.%%L >>%%A.%%B.%%C.%%L_Ping.log"
)
)
)

timeout 10
for /R %%_ IN (*_Ping.log) DO (
FOR /F "Tokens=*" %%l IN ('
TYPE "%%~f_" | FIND /I /V "Pinging " | FIND /I /V "Ping Statistics FOR" | FIND /I /V "Packets: " | FIND /I /V "Approximate round trip " | FIND /I /V "Minimum = "
') DO (
FOR /F "Tokens=1 delims=_" %%# IN ("%%~n_") DO (
CALL ECHO. %%TIME%% - %%# -- %%l
)
)
)


Related Topics



Leave a reply



Submit