Determine server availability using a program acting as a UDP echo - python

I'm using Xen Hypervisor in ubuntu and I have a VM. When live-migrating vm to another host, the vm will be Unavailable from a range of about a few milliseconds to no more than a few seconds tops (depending on the environment). I need to be able to determine that short time as accurate as possible. so I need to 'somehow' check the vm say every 100 milliseconds. and the number of times in which I find the vm UNAVAILABLE continuously, multiplied by 100, will be the total milliseconds that my vm was down.
ping doesn't work since it's not being accurate and in case of vm being unavailable, ping command waits and retries sending ICMP packets and This spoils the goal to find out if the server is available in that EXACT CHECKING MOMENT. Plus I asked a question in here and the feedback was "don't use ping!"
so NO USING PING!
I need to write my own piece of code in python/perl/whathever which could do the job. How could I do that?

ping doesn't work since it's not being accurate and in case of vm being unavailable, ping command waits and retries sending ICMP packets
That's the default, but you can tell it to only send one.
$ ping -q -c 1 9.9.9.9 >/dev/null
$ echo $?
1
$ ping -q -c 1 8.8.8.8 >/dev/null
$ echo $?
0
So
while ! ping -q -c 1 -W 1 x.x.x.x >/dev/null ; do true ; done
Plus I asked a question in here and the feedback was "don't use ping!"
so NO USING PING!
Yet you asked for a (UDP-based) ping instead of a means of checking if the needed service is up.

Here is a ZeroMQ UDP ping function adapted from UDP discovery, model 1 in Python by disabling broadcasting, pinging just one IP address, returning after the first reply, adding a counter to limit the number of ping attempts and adding extra error handling during message receive. The latter was done since one host in my net forcibly closed the connection causing socket.error: [Errno 10054]. It has been tested and found to work with Python 2.7.10 and 3.4.3.
from __future__ import print_function
import os
import socket
import sys
import time
import zmq
def udpping(ip):
PING_PORT_NUMBER = 9999
PING_MSG_SIZE = 1
PING_INTERVAL = 1 # once per second, sets minimum initial timeout
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# uncomment the line below for broadcast to 255.255.255.255
# sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.bind(('', PING_PORT_NUMBER))
poller = zmq.Poller()
poller.register(sock, zmq.POLLIN)
ping_at = time.time()
limit = 5 # only limit ping attempts will be made
count = 0
while True:
count += 1
timeout = ping_at - time.time()
if timeout < 0:
timeout = 0
try:
events = dict(poller.poll(1000* timeout))
except KeyboardInterrupt:
return (1,None,None)
try:
if sock.fileno() in events:
msg, addrinfo = sock.recvfrom(PING_MSG_SIZE)
return (2,"found %s:%d" % addrinfo,None)
except socket.error as e:
return (3,'error during msg receive:',e)
if time.time() >= ping_at:
# the line below is for broadcasting
# sock.sendto(b'!', 0, ("255.255.255.255", PING_PORT_NUMBER))
sock.sendto(b'!', 0, (ip, PING_PORT_NUMBER))
ping_at = time.time() + PING_INTERVAL
if count == limit:
return (4,limit,None)
ip = '192.168.159.21'
c,m,e = udpping(ip)
Below is shown output handling. For binary decision, the ping succeeded only if c == 2.
if c == 1:
print('ping attempt stopped by KeyboardInterrupt')
elif c == 2:
print(m)
elif c == 3:
print(m,e)
elif c == 4:
print('no response from',ip,'after',m,'attempts')
# prints 'found 192.168.159.21:9999' immediately in my net

Related

Python Sockets Left in UNCONN State

I am facing an issue where my sockets in ubuntu are left in UNCONN State:
ss -s
And below are the state for better visibility
ss -au
I am using a python code that is trying to use sockets to receive audio streams and the basic function to create a socket looks like the below :
def find_incoming_port_multiplier(self, ip, alias,port=0, min_port=1000, max_port=11000):
if min_port > 0:
port = min_port + alias
while port <= max_port:
if port in Controller.port_info:
logger.info('port already present in port_info' + str(port))
port += 1000
continue
try:
listeningAddress = (ip, port)
logger.info("Looking for PORT " + str(listeningAddress))
datagramSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#End the socket in 30 seconds ... just a precaution
datagramSocket.settimeout(30)
datagramSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
logger.info("Trying Binding... " + str(listeningAddress))
datagramSocket.bind(listeningAddress)
logger.info("Final Port Allocated " + str(listeningAddress))
return datagramSocket, datagramSocket.getsockname()[1]
except OSError:
logger.info('PORT ' + str(port) + ' already in use')
port += 1000
raise IOError('no free ports')
The socket object from the above function is used by a new thread and ends once the processing is done.
I am still not determined whether it's something to do with linux OR how the socket is created.
Killing the code forcefully all the sockets are released. Interestingly the same issue is not present if the OS is windows.
SO JUST TO EXPLAIN FURTHER AS REQUESTED:
The Function above shared an unconnected socket
The unconnected socket is set as a variable of a class, and thread is started to process it
The socket is finally closed at the end
try:
while (True):
self.pi.incoming_sock.settimeout(5)
try:
message, sourceAddress = self.pi.incoming_sock.recvfrom(self.incoming_packet_size)
except:
logger.info('Stream closed.. exiting loop')
break
self.stream.write(message)
except:
logger.exception(traceback.print_exc())
finally:
if self.pi is not None and self.pi.incoming_sock is not None:
self.pi.incoming_sock.shutdown(socket.SHUT_RDWR)
self.pi.incoming_sock.close()
I was earlier assuming that the threads are not dying but using the Htop command on Linux I can see that at any point the threads are no more than 200 but sockets reach around 52K and then the process hangs.

DNS server using scapy

i wrote a python DNS server using Scapy and i have a problem.
i sniff a DNS packet (by filter to A or PTR types) and than i wanna send a relevant response.
i use two computers and in one of them i use nslookup www.google.com
it shows me "request time out error" - timeout was 2 seconds.
BYW i received this packet and i sent a one!! but the other computer didn't get this on time i guess.
i tried to change the timeout to 20 seconds but it just wait 20 seconds.. and than shows an error..
and my server get the packet after this 20 seconds.
how can i solve this problem and what cause it!!
Thanks from advance :)
here some of my code (in the may relevant parts):
def filter_dns(packet):
return DNS in packet and packet[DNS].opcode == 0 and (packet[DNSQR].qtype == 1 or packet[DNSQR].qtype == 12)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind((MY_IP, PORT))
print "Server started successfully! Waiting for data..."
packets = sniff(count=1, lfilter=filter_dns)
def send_typeA(packet, result):
mypacket = IP(dst=Ip_Client) / DNS() / DNSRR()
mypacket[DNSRR].rrname = packet[DNSQR].qname
mypacket[DNSRR].rdata = result # result is the ip of the domain name
mypacket[DNSRR].ttl = 100
print mypacket.show()
send(mypacket)

Python pexpect Check if server is up

I am automating few configuration steps on CentOS. In order to do so i need to reboot the system also. I am invoking the "reboot" command the via python pexepct, however i need to wait till the systems boots up for remaining script to executes. For that i am have written this small piece of code.
while True:
result = commands.getoutput("ping -c 4 192.168.36.134")
if result.find("Unreachable") == 1:
result = False
print 'Rebooting the Systems.'
else:
result = True
print 'Connected!'
break
Is there any better way to do this? Also, can we achieve this with the pexepct itself?
You can try this:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# wait host to finish reboot, check specific port connection (usually ssh port if you want to exec remote commands)
while True:
try:
s.connect(('hostname', 22))
print "Port 22 reachable"
break
except socket.error as e:
print "rebooting..."
# continue
s.close()
This example is more effective then using ping

Python Check if Condition in Loop Changes

I have created a small python script to monitor if an OS (Linux) is able to ping a certain ip address of 10.0.0.1 (router on LAN). This first script works fine but I have to execute the script to have it evaluate the condition each time.
I think I understand that this script simply exits after the condition is evaluated. I tried including a while else loop in the second example below that evaluates the return of the OS call and place this function call in the loop to keep checking if the router was plugged back into the LAN.
Is the correct approach to define a function to evaluate the ping status? I tried adding a function in the last example but it doesn't seem to update.
My goal is to run this script once and have it detect is 10.0.0.1 is up or down when I unplug it from the network or plug it back in.
import os
import time
ip = "10.0.0.1"
response = os.system("ping -c 1 " +ip)
if response != 0:
print ip, 'is down'
time.sleep(3)
else:
print ip, 'is up!'
I tried adding the while else loop but the condition doesn't seem to get updated.
import os
import time
ip = "10.0.0.1"
response = os.system("ping -c 1 " +ip)
while response != 0:
print ip, 'is down'
time.sleep(3)
else:
print ip, 'is up!'
I even tried defining a fuction to evaluate the condition for every iteration of the loop..
import os
import time
ip = "10.0.0.1"
response = os.system("ping -c 1 " +ip)
def checkstatus():
response = os.system("ping -c 1 " +ip)
while response != 0:
print ip, 'is down'
time.sleep(3)
checkstatus()# trying to evaluate if status has changed
while response != 1:
print ip, 'is up'
time.sleep(3)
checkstatus()# trying to evaluate if status has changed
EDIT
This is how I changed the script and now it works..Just need to slow down the network unreachable stdout.
import os
import time
ip = "10.0.0.1"
while True:
response = os.system("ping -c 1 " + ip)
if response == 0:
print'connected'
time.sleep(.5)
New output NOTICE print statement connected appears as connected
--- 10.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.383/0.383/0.383/0.000 ms
**connected**
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.308 ms
--- 10.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.308/0.308/0.308/0.000 ms
***connected***
New Output after unplugged for a few seconds (floods screen)
connect: Network is unreachable
connect: Network is unreachable
connect: Network is unreachable
connect: Network is unreachable
connect: Network is unreachable
Nothing too bad but worried this will use many CPU cycles as this script will
be running on a raspberry pi.
The mistake you make is that you look for changes in response, which doesn't reflect the network status changes unless you repeat the assignment to it via response = os.system("ping -c 1 " +ip).
Apart from including the assignment inside your loop (as suggested by DeepSpace) you can also change the loop check itself to correctly reflect network status changes (according to your method):
while os.system("ping -c 1 " +ip):
print ip, 'is down'
time.sleep(3)
Similarly, when using checkstatus() you'd need to either include response = checkstatus() in the loop or use it in the check: while checkstatus():.
The last version of your code has a couple of extra problems:
typically the return code of a program is 0 on success, but it may have multiple non-zero return codes, so your != 1 check might not work as you'd expect, better use == 0 check instead.
the sequenced potentially endless while statements won't actually complete unless a certain sequence of the connection state happens. Since your goal is to simply run once and find out the current state of the connection the code would better not depend on such sequence.
Maybe something like this instead?
if os.system("ping -c 1 " +ip):
print ip, 'is down'
else:
print ip, 'is up'
Or, if you prefer one-liners:
print ip, 'is ' + 'down' if os.system("ping -c 1 " +ip) else 'up'
For your second attempt (with the function), I believe what is happening is that response is being defined as a local variable of the function checkstatus, which is separate from the global varriable response. To reassign the global from the function (and not make a local variable), add the line global response to the top of the function body.
The naive way would be to use while True:
ip = "10.0.0.1"
while True:
response = os.system("ping -c 1 " + ip)
if response == 0:
break # or do anything else
time.sleep(3)
A better way is to have the script make a single call to os.system then use crontab or Windows Scheduler to run it every X amount of time.

Pinging servers in Python

In Python, is there a way to ping a server through ICMP and return TRUE if the server responds, or FALSE if there is no response?
If you don't need to support Windows, here's a really concise way to do it:
import os
hostname = "google.com" #example
response = os.system("ping -c 1 " + hostname)
#and then check the response...
if response == 0:
print hostname, 'is up!'
else:
print hostname, 'is down!'
This works because ping returns a non-zero value if the connection fails. (The return value actually differs depending on the network error.) You could also change the ping timeout (in seconds) using the '-t' option. Note, this will output text to the console.
This function works in any OS (Unix, Linux, macOS, and Windows)
Python 2 and Python 3
EDITS:
By #radato os.system was replaced by subprocess.call. This avoids shell injection vulnerability in cases where your hostname string might not be validated.
import platform # For getting the operating system name
import subprocess # For executing a shell command
def ping(host):
"""
Returns True if host (str) responds to a ping request.
Remember that a host may not respond to a ping (ICMP) request even if the host name is valid.
"""
# Option for the number of packets as a function of
param = '-n' if platform.system().lower()=='windows' else '-c'
# Building the command. Ex: "ping -c 1 google.com"
command = ['ping', param, '1', host]
return subprocess.call(command) == 0
Note that, according to #ikrase on Windows this function will still return True if you get a Destination Host Unreachable error.
Explanation
The command is ping in both Windows and Unix-like systems.
The option -n (Windows) or -c (Unix) controls the number of packets which in this example was set to 1.
platform.system() returns the platform name. Ex. 'Darwin' on macOS.
subprocess.call() performs a system call. Ex. subprocess.call(['ls','-l']).
There is a module called pyping that can do this. It can be installed with pip
pip install pyping
It is pretty simple to use, however, when using this module, you need root access due to the fact that it is crafting raw packets under the hood.
import pyping
r = pyping.ping('google.com')
if r.ret_code == 0:
print("Success")
else:
print("Failed with {}".format(r.ret_code))
import subprocess
ping_response = subprocess.Popen(["/bin/ping", "-c1", "-w100", "192.168.0.1"], stdout=subprocess.PIPE).stdout.read()
For python3 there's a very simple and convenient python module ping3: (pip install ping3, needs root privileges).
from ping3 import ping, verbose_ping
ping('example.com') # Returns delay in seconds.
>>> 0.215697261510079666
This module allows for the customization of some parameters as well.
Programmatic ICMP ping is complicated due to the elevated privileges required to send raw ICMP packets, and calling ping binary is ugly. For server monitoring, you can achieve the same result using a technique called TCP ping:
# pip3 install tcping
>>> from tcping import Ping
# Ping(host, port, timeout)
>>> ping = Ping('212.69.63.54', 22, 60)
>>> ping.ping(3)
Connected to 212.69.63.54[:22]: seq=1 time=23.71 ms
Connected to 212.69.63.54[:22]: seq=2 time=24.38 ms
Connected to 212.69.63.54[:22]: seq=3 time=24.00 ms
Internally, this simply establishes a TCP connection to the target server and drops it immediately, measuring time elapsed. This particular implementation is a bit limited in that it doesn't handle closed ports but for your own servers it works pretty well.
Because I like to have my Python program universal on version 2.7 and 3.x and on platform Linux, Mac OS and Windows, I had to modify the existing examples.
# shebang does not work over all platforms
# ping.py 2016-02-25 Rudolf
# subprocess.call() is preferred to os.system()
# works under Python 2.7 and 3.4
# works under Linux, Mac OS, Windows
def ping(host):
"""
Returns True if host responds to a ping request
"""
import subprocess, platform
# Ping parameters as function of OS
ping_str = "-n 1" if platform.system().lower()=="windows" else "-c 1"
args = "ping " + " " + ping_str + " " + host
need_sh = False if platform.system().lower()=="windows" else True
# Ping
return subprocess.call(args, shell=need_sh) == 0
# test call
print(ping("192.168.17.142"))
using socket package in python3:
import socket
def ping_server(server: str, port: int, timeout=3):
"""ping server"""
try:
socket.setdefaulttimeout(timeout)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((server, port))
except OSError as error:
return False
else:
s.close()
return True
My version of a ping function:
Works on Python 3.5 and later, on Windows and Linux.
On Windows, returns False if the ping command fails with "Destination Host Unreachable".
And does not show any output, either as a pop-up window or in command line.
import platform, subprocess
def ping(host_or_ip, packets=1, timeout=1000):
''' Calls system "ping" command, returns True if ping succeeds.
Required parameter: host_or_ip (str, address of host to ping)
Optional parameters: packets (int, number of retries), timeout (int, ms to wait for response)
Does not show any output, either as popup window or in command line.
Python 3.5+, Windows and Linux compatible
'''
# The ping command is the same for Windows and Linux, except for the "number of packets" flag.
if platform.system().lower() == 'windows':
command = ['ping', '-n', str(packets), '-w', str(timeout), host_or_ip]
# run parameters: capture output, discard error messages, do not show window
result = subprocess.run(command, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, creationflags=0x08000000)
# 0x0800000 is a windows-only Popen flag to specify that a new process will not create a window.
# On Python 3.7+, you can use a subprocess constant:
# result = subprocess.run(command, capture_output=True, creationflags=subprocess.CREATE_NO_WINDOW)
# On windows 7+, ping returns 0 (ok) when host is not reachable; to be sure host is responding,
# we search the text "TTL=" on the command output. If it's there, the ping really had a response.
return result.returncode == 0 and b'TTL=' in result.stdout
else:
command = ['ping', '-c', str(packets), '-w', str(timeout), host_or_ip]
# run parameters: discard output and error messages
result = subprocess.run(command, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return result.returncode == 0
Feel free to use it as you will.
#!/usr/bin/python3
import subprocess as sp
def ipcheck():
status,result = sp.getstatusoutput("ping -c1 -w2 " + str(pop))
if status == 0:
print("System " + str(pop) + " is UP !")
else:
print("System " + str(pop) + " is DOWN !")
pop = input("Enter the ip address: ")
ipcheck()
After looking around, I ended up writing my own ping module, which is designed to monitor large numbers of addresses, is asynchronous and doesn't use a lot of system resources. You can find it here: https://github.com/romana/multi-ping/ It's Apache licensed, so you can use it in your project in any way you see fit.
The main reasons for implementing my own are the restrictions of the other approaches:
Many of the solutions mentioned here require an exec out to a command line utility. This is quite inefficient and resource hungry if you need to monitor large numbers of IP addresses.
Others mention some older python ping modules. I looked at those and in the end, they all had some issue or the other (such as not correctly setting packet IDs) and didn't handle the ping-ing of large numbers of addresses.
I resolve this with:
def ping(self, host):
res = False
ping_param = "-n 1" if system_name().lower() == "windows" else "-c 1"
resultado = os.popen("ping " + ping_param + " " + host).read()
if "TTL=" in resultado:
res = True
return res
"TTL" is the way to know if the ping is correctly.
Saludos
Make Sure pyping is installed or install it pip install pyping
#!/usr/bin/python
import pyping
response = pyping.ping('Your IP')
if response.ret_code == 0:
print("reachable")
else:
print("unreachable")
If your server does not support ICMP (firewall might block it), it most probably still offers a service on a TCP port. In this case, you can perform a TCP ping1 (platform independently and without installing additional python modules) like this:
import socket
def isReachable(ipOrName, port, timeout=2):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
try:
s.connect((ipOrName, int(port)))
s.shutdown(socket.SHUT_RDWR)
return True
except:
return False
finally:
s.close()
The code is taken and only slightly modified from here.
1 A TCP ping does not really exist as a ping is performed with ICMP on ISO/OSI layer 3. A TCP ping is performed on ISO/OSI layer 4. It just tries to connect to a TCP port in the most basic way, that it is not transmitting any data, but closing the connection immediately after connecting.
#!/usr/bin/python3
import subprocess as sp
ip = "192.168.122.60"
status,result = sp.getstatusoutput("ping -c1 -w2 " + ip)
if status == 0:
print("System " + ip + " is UP !")
else:
print("System " + ip + " is DOWN !")
My reduction using ideas from answers in this post but only using the newer recommended subprocess module and python3:
import subprocess
import platform
operating_sys = platform.system()
nas = '192.168.0.10'
def ping(ip):
# ping_command = ['ping', ip, '-n', '1'] instead of ping_command = ['ping', ip, '-n 1'] for Windows
ping_command = ['ping', ip, '-n', '1'] if operating_sys == 'Windows' else ['ping', ip, '-c 1']
shell_needed = True if operating_sys == 'Windows' else False
ping_output = subprocess.run(ping_command,shell=shell_needed,stdout=subprocess.PIPE)
success = ping_output.returncode
return True if success == 0 else False
out = ping(nas)
print(out)
This script works on Windows, and should work on other OSes :
It works on Windows, Debian, and macosx, need a test on solaris.
import os
import platform
def isUp(hostname):
giveFeedback = False
if platform.system() == "Windows":
response = os.system("ping "+hostname+" -n 1")
else:
response = os.system("ping -c 1 " + hostname)
isUpBool = False
if response == 0:
if giveFeedback:
print hostname, 'is up!'
isUpBool = True
else:
if giveFeedback:
print hostname, 'is down!'
return isUpBool
print(isUp("example.com")) #Example domain
print(isUp("localhost")) #Your computer
print(isUp("invalid.example.com")) #Unresolvable hostname: https://tools.ietf.org/html/rfc6761
print(isUp("192.168.1.1")) #Pings local router
print(isUp("192.168.1.135")) #Pings a local computer - will differ for your network
I ended up finding this question regarding a similar scenario. I tried out pyping but the example given by Naveen didn't work for me in Windows under Python 2.7.
An example that worked for me is:
import pyping
response = pyping.send('Your IP')
if response['ret_code'] == 0:
print("reachable")
else:
print("unreachable")
Using Multi-ping (pip install multiPing) I made this simple code (simply copy and paste if you will!):
from multiping import MultiPing
def ping(host,n = 0):
if(n>0):
avg = 0
for i in range (n):
avg += ping(host)
avg = avg/n
# Create a MultiPing object to test hosts / addresses
mp = MultiPing([host])
# Send the pings to those addresses
mp.send()
# With a 1 second timout, wait for responses (may return sooner if all
# results are received).
responses, no_responses = mp.receive(1)
for addr, rtt in responses.items():
RTT = rtt
if no_responses:
# Sending pings once more, but just to those addresses that have not
# responded, yet.
mp.send()
responses, no_responses = mp.receive(1)
RTT = -1
return RTT
Usage:
#Getting the latency average (in seconds) of host '192.168.0.123' using 10 samples
ping('192.168.0.123',10)
If you want a single sample, the second parameter "10" can be ignored!
Hope it helps!
I needed a faster ping sweep and I didn't want to use any external libraries, so I resolved to using concurrency using built-in asyncio.
This code requires python 3.7+ and is made and tested on Linux only. It won't work on Windows but I am sure you can easily change it to work on Windows.
I ain't an expert with asyncio but I used this great article Speed Up Your Python Program With Concurrency and I came up with these lines of codes. I tried to make it as simple as possible, so most likely you will need to add more code to it to suit your needs.
It doesn't return true or false, I thought it would be more convenient just to make it print the IP that responds to a ping request. I think it is pretty fast, pinging 255 ips in nearly 10 seconds.
#!/usr/bin/python3
import asyncio
async def ping(host):
"""
Prints the hosts that respond to ping request
"""
ping_process = await asyncio.create_subprocess_shell("ping -c 1 " + host + " > /dev/null 2>&1")
await ping_process.wait()
if ping_process.returncode == 0:
print(host)
return
async def ping_all():
tasks = []
for i in range(1,255):
ip = "192.168.1.{}".format(i)
task = asyncio.ensure_future(ping(ip))
tasks.append(task)
await asyncio.gather(*tasks, return_exceptions = True)
asyncio.run(ping_all())
Sample output:
192.168.1.1
192.168.1.3
192.168.1.102
192.168.1.106
192.168.1.6
Note that the IPs are not in order, as the IP is printed as soon it replies, so the one that responds first gets printed first.
on linux, it's possible to create ICMP datagram (not raw) sockets without being root (or setuid or CAP_NET_RAW): https://unix.stackexchange.com/a/592914. I ended up with
$ id
uid=1000(raylu) gid=1000(raylu) [...]
$ sudo sysctl net.ipv4.ping_group_range='1000 1000'
import socket
import struct
import time
def main():
ping('192.168.1.10')
def ping(destination):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.getprotobyname('icmp'))
sock.settimeout(10.0)
start_time = time.time_ns() # python 3.7+ only
payload = struct.pack('L', start_time)
sock.sendto(encode(payload), (destination, 0))
while (time.time_ns() - start_time) // 1_000_000_000 < 10:
try:
data, source = sock.recvfrom(256)
except socket.timeout:
print('timed out')
return
message_type, message_code, check, identifier, sequence_number = struct.unpack('bbHHh', data[:8])
if source == (destination, 0) and message_type == ICMP.ECHO_REPLY and data[8:] == payload:
print((time.time_ns() - start_time) // 1_000_000, 'ms')
break
else:
print('got unexpected packet from %s:' % source[0], message_type, data[8:])
else:
print('timed out')
def encode(payload: bytes):
# calculate checksum with check set to 0
checksum = calc_checksum(icmp_header(ICMP.ECHO_REQUEST, 0, 0, 1, 1) + payload)
# craft the packet again with the checksum set
return icmp_header(ICMP.ECHO_REQUEST, 0, checksum, 1, 1) + payload
def icmp_header(message_type, message_code, check, identifier, sequence_number) -> bytes:
return struct.pack('bbHHh', message_type, message_code, check, identifier, sequence_number)
def calc_checksum(data: bytes) -> int:
'''RFC 1071'''
# code stolen from https://github.com/alessandromaggio/pythonping/blob/a59ce65a/pythonping/icmp.py#L8
'''
MIT License
Copyright (c) 2018 Alessandro Maggio
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
'''
subtotal = 0
for i in range(0, len(data)-1, 2):
subtotal += (data[i] << 8) + data[i+1]
if len(data) % 2:
subtotal += (data[len(data)-1] << 8)
while subtotal >> 16:
subtotal = (subtotal & 0xFFFF) + (subtotal >> 16)
check = ~subtotal
return ((check << 8) & 0xFF00) | ((check >> 8) & 0x00FF)
class ICMP:
ECHO_REPLY = 0
ECHO_REQUEST = 8
though many of the packages other answers have suggested here would work too
Seems simple enough, but gave me fits. I kept getting "icmp open socket operation not permitted" or else the solutions would hang up if the server was off line. If, however, what you want to know is that the server is alive and you are running a web server on that server, then curl will do the job. If you have ssh and certificates, then ssh and a simple command will suffice. Here is the code:
from easyprocess import EasyProcess # as root: pip install EasyProcess
def ping(ip):
ping="ssh %s date;exit"%(ip) # test ssh alive or
ping="curl -IL %s"%(ip) # test if http alive
response=len(EasyProcess(ping).call(timeout=2).stdout)
return response #integer 0 if no response in 2 seconds
Use this it's tested on python 2.7 and works fine it returns ping time in milliseconds if success and return False on fail.
import platform,subproccess,re
def Ping(hostname,timeout):
if platform.system() == "Windows":
command="ping "+hostname+" -n 1 -w "+str(timeout*1000)
else:
command="ping -i "+str(timeout)+" -c 1 " + hostname
proccess = subprocess.Popen(command, stdout=subprocess.PIPE)
matches=re.match('.*time=([0-9]+)ms.*', proccess.stdout.read(),re.DOTALL)
if matches:
return matches.group(1)
else:
return False
I had similar requirement so i implemented it as shown below. It is tested on Windows 64 bit and Linux.
import subprocess
def systemCommand(Command):
Output = ""
Error = ""
try:
Output = subprocess.check_output(Command,stderr = subprocess.STDOUT,shell='True')
except subprocess.CalledProcessError as e:
#Invalid command raises this exception
Error = e.output
if Output:
Stdout = Output.split("\n")
else:
Stdout = []
if Error:
Stderr = Error.split("\n")
else:
Stderr = []
return (Stdout,Stderr)
#in main
Host = "ip to ping"
NoOfPackets = 2
Timeout = 5000 #in milliseconds
#Command for windows
Command = 'ping -n {0} -w {1} {2}'.format(NoOfPackets,Timeout,Host)
#Command for linux
#Command = 'ping -c {0} -w {1} {2}'.format(NoOfPackets,Timeout,Host)
Stdout,Stderr = systemCommand(Command)
if Stdout:
print("Host [{}] is reachable.".format(Host))
else:
print("Host [{}] is unreachable.".format(Host))
When IP is not reachable subprocess.check_output() raises an exception. Extra verification can be done by extracting information from output line 'Packets: Sent = 2, Received = 2, Lost = 0 (0% loss)'.
WINDOWS ONLY - Can't believe no-ones cracked open Win32_PingStatus
Using a simple WMI query we return an object full of really detailed info for free
import wmi
# new WMI object
c = wmi.WMI()
# here is where the ping actually is triggered
x = c.Win32_PingStatus(Address='google.com')
# how big is this thing? - 1 element
print 'length x: ' ,len(x)
#lets look at the object 'WMI Object:\n'
print x
#print out the whole returned object
# only x[0] element has values in it
print '\nPrint Whole Object - can directly reference the field names:\n'
for i in x:
print i
#just a single field in the object - Method 1
print 'Method 1 ( i is actually x[0] ) :'
for i in x:
print 'Response:\t', i.ResponseTime, 'ms'
print 'TTL:\t', i.TimeToLive
#or better yet directly access the field you want
print '\npinged ', x[0].ProtocolAddress, ' and got reply in ', x[0].ResponseTime, 'ms'
sample output
Here's a solution using Python's subprocess module and the ping CLI tool provided by the underlying OS. Tested on Windows and Linux. Support setting a network timeout. Doesn't need root privileges (at least on Windows and Linux).
import platform
import subprocess
def ping(host, network_timeout=3):
"""Send a ping packet to the specified host, using the system "ping" command."""
args = [
'ping'
]
platform_os = platform.system().lower()
if platform_os == 'windows':
args.extend(['-n', '1'])
args.extend(['-w', str(network_timeout * 1000)])
elif platform_os in ('linux', 'darwin'):
args.extend(['-c', '1'])
args.extend(['-W', str(network_timeout)])
else:
raise NotImplemented('Unsupported OS: {}'.format(platform_os))
args.append(host)
try:
if platform_os == 'windows':
output = subprocess.run(args, check=True, universal_newlines=True).stdout
if output and 'TTL' not in output:
return False
else:
subprocess.run(args, check=True)
return True
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
return False
My take borrowing from other answers. Attempt to simplify and minimize queries.
import platform, os
def ping(host):
result = os.popen(' '.join(("ping", ping.param, host))).read()
return 'ttl=' in result.lower()
ping.param = "-n 1" if platform.system().lower() == "windows" else "-c 1"
EDIT: ignoring case in return as per comment by Olivier B.
import os #to get clear screen
import subprocess as sp #to get system ping
os.system("clear") #clear screen
print('Wait or Press Ctrl+Z to Terminate\n') #notice to terminate or wait
for i in range(255): #0 to 255 loop
ip='192.168.1.'+str(i) #concatenating str and int
s,r=sp.getstatusoutput("ping -c1 -w2 " + ip) #ping and store status in s
if s==0: #if status is 0 equal to pass
print(ip+" is UP ✓ ") #output
else: #if status is not 0 equal to fail
pass #skip and try next ip from loop
Ping them all in windows or linux, return a sorted list. This is a hybrid/fix from the responses #Ahmed Essam and #Arno.
import asyncio
import re
import platform
isWindows = platform.system()
async def ping(host):
cmd = 'ping {} {} 1'.format(host, '-n' if isWindows else '-c')
ping_proc = \
await asyncio.create_subprocess_shell(cmd, stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE)
stdout, stderr = await ping_proc.communicate()
outstr = stdout.decode()
if ping_proc.returncode == 0:
delay = int(re.search(r'(?:time=)([\d]*)', outstr).group(1)) if 'time=' in outstr else -1
if delay >= 0:
# print('{} {}ms'.format(host, delay))
return [host, delay]
return [host, None]
async def ping_all():
tasks = []
for i in range(1, 256):
ip = "192.168.1.{}".format(i)
task = asyncio.ensure_future(ping(ip))
tasks.append(task)
retList = await asyncio.gather(*tasks, return_exceptions=True)
retList = [x for x in retList if x[1] is not None]
retList.sort(key=lambda x: int(x[0].split('.')[-1]))
return retList
loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)
pingRet = loop.run_until_complete(ping_all())
for ip, d in pingRet:
print('{:<16s} {}ms'.format(ip, d))
My own method that combines several answers above:
def ping(host, show_log=False, package_count=1):
ping.param = "-n" if platform.system().lower() == 'windows' else "-c"
result = subprocess.run(['ping', ping.param, str(package_count), host],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
output = result.stdout
if show_log:
print('return code: ', result.returncode)
print(output.decode("utf-8"))
return result.returncode == 0 and (b'TTL=' in output or b'ttl=' in output)
Tested on OSX Monterey.

Categories