valentinbelyn / icmplib Goto Github PK
View Code? Open in Web Editor NEWEasily forge ICMP packets and make your own ping and traceroute.
License: GNU Lesser General Public License v3.0
Easily forge ICMP packets and make your own ping and traceroute.
License: GNU Lesser General Public License v3.0
Dear Valentin,
Can you add to a icmplib library function receive MAC address during multiping?
Thanks.
python 3.6 also supports asyncio, can you support python3.6 version, please
I'm running icmplib on a windows 10 laptop with the basic traceroute example code and what the code is reporting back is just the final hop, the list has a single entry. However if I run up wireshark I'm seeing three pings and their corresponding TTL exceeded reply messages. So for whatever reason the only response that seems to be making it up the stack is the echo reply not the TTL exceeded messages.
I think this is the cause of home-assistant/core#40232
The timeout is being linked to the count.
>>> import time
>>> from icmplib import ping
>>> time.time() ; ping("1.2.3.4", timeout=2, count=30) ; time.time()
1602340930.801749
<Host [1.2.3.4]>
1602341005.2304416
>>>
>>> time.time() ; ping("1.2.3.4", timeout=2) ; time.time()
1602340720.058677
<Host [1.2.3.4]>
1602340728.365908
>>> time.time() ; ping("1.2.3.4", timeout=1) ; time.time()
1602340734.0859458
<Host [1.2.3.4]>
1602340738.0910802
>>>
Hi,
I use python 3.8.2 on windows and everytime I try to execute traceroute I'll get a len(hops) == 1
Are there any compatibiliy issues on Windows systems?
Environment:
Win 10 Pro
Python 3.8.2
Thanks for your help and
regards,
Coffeemaster
Thank you so much for your effort on icmplib.
I want to send traceroute with ipv6.
I tried with
: hops = traceroute('2620:100:6040:18::a27d:f812', timeout=1, fast=True, family=6)
But I got the error:
temp18@temp18-Predator-PH315-52:~/ECN_project/icmplib/examples$ sudo python3 traceroute.py Traceback (most recent call last): File "traceroute.py", line 20, in <module> hops = traceroute('2620:100:6040:18::a27d:f812', timeout=1, fast=True, family=6) File "/home/temp18/.local/lib/python3.6/site-packages/icmplib/traceroute.py", line 178, in traceroute **kwargs) TypeError: __init__() got an unexpected keyword argument 'family'
Cloud you please help on this? Thank you.
I have a ICMPSocket
. I have to use a .setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, b"iface")
on the underlying socket. What is the recommended way to do that?
s = ICMPv6Socket(); s._sock.setsockopt(...)
makes an access to a private attribute.ICMPSocket
and add such a functionality is possible and easy; but why is it not done in icmplib
itself then?_set_ttl
and _set_traffic_class
) is too heavy as the number of supported options increaseicmpsocket.setsockopt(...)
) that just forwards the call to self._sock.setsockopt(...)
is possible, while it adds a function call.self._sock
to a public attribute is another possible solution.icmplib
won't support it (which I cannot accept for my personal project—but that is my personal problem then. 😉 )I would prefer 2.b supported by icmplib
.
Is it possible to use this library without root permission?
I'm using Kali Linux kali 5.16.0-kali1-amd64 #1 SMP PREEMPT Debian 5.16.7-2kali1 (2022-02-10) x86_64 GNU/Linux
with Python 3.9 and icmplib-3.0.3. I can run ping
in my terminal. I've checked
$ ls -l $(which ping)
-rwxr-xr-x 1 root root 81600 Feb 5 05:37 /usr/bin/ping
As you can see, SUID and SGID isn't set. I've checked
$ sysctl net.ipv4.ping_group_range
net.ipv4.ping_group_range = 1 0
Obviously, ping
can be run without root permission.
I've tried
from icmplib import ping
ping('192.168.178.1', privileged=False)
and got
Traceback (most recent call last):
File "/workdir/lib/python3.9/site-packages/icmplib/sockets.py", line 88, in __init__
self._sock = self._create_socket(
File "/workdir/lib/python3.9/site-packages/icmplib/sockets.py", line 486, in _create_socket
return socket.socket(
File "/usr/lib/python3.9/socket.py", line 232, in __init__
_socket.socket.__init__(self, family, type, proto, fileno)
PermissionError: [Errno 13] Permission denied
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/workdir/main.py", line 5, in <module>
ping('192.168.178.1', privileged=False)
File "/workdir/lib/python3.9/site-packages/icmplib/ping.py", line 141, in ping
with _Socket(source, privileged) as sock:
File "/workdir/lib/python3.9/site-packages/icmplib/sockets.py", line 97, in __init__
raise SocketPermissionError(privileged)
icmplib.exceptions.SocketPermissionError: A prior configuration of your OS is required to use ICMP sockets without root privileges. Read more on https://github.com/ValentinBELYN/icmplib
I don't have permissions to change
sudo sysctl -w net.ipv4.ping_group_range='0 2147483647'
Is there a way to use this library? Is this problem related to the way this library works or is it a general Python problem?
Hello and thank you for your development of icmplib.
I'm using it as part of a tool for diagnostic purposes. I wanted to ask if it is possible to include these two parameters as part of future development. First one is a "df" parameter as a feature, so that if set to True, will enable the Don't Fragment flag in the IP header. Second one is a ttl return value, corresponding to the ttl field value in received packet. Or maybe there is something related to the above but I'm missing them.
Thanks again and regards.
Hi,
First of all, thank you for the library. I have been testing PING
and Multiping
, but it seems the privileged attribute is not working on macOS 11.6.5 with python 3.9.7
I'm calling the function as this
hosts = multiping( devices, count=3,interval=0.5,privileged=False)
if I used python as regular user, then I got
PermissionError: [Errno 1] Operation not permitted
no matter if I set ptivileged True or False.
If I try to use sudo python
(to elevate privilege) then I got this error
request = ICMPRequest(TypeError: __init__() got an unexpected keyword argument 'privileged
again it doesn't matter if I passed True or False to the argument.
The same behavior is with ping
function. With this sentence
host = ping('10.74.200.1', privileged=False)
an error is raised,
icmplib.exceptions.SocketPermissionError: Root privileges are required to create the socket
and also ther is no effect on change the privileged argument to True or False.
this are the version installed
icmplib==3.0.3
icmplibv2==1.0.6
Thanks again for any hints to this situation
Works for macOS 10.15 (with patch):
>>> print(icmplib.traceroute("www.google.com", privileged=False))
[<Hop 1 [...]>, <Hop 2 [...]>, <Hop 3 [...]>, <Hop 4 [188.234.131.158]>, <Hop 5 [188.234.131.159]>, <Hop 6 [172.253.76.91]>, <Hop 7 [74.125.244.181]>, <Hop 8 [72.14.232.84]>, <Hop 9 [142.251.61.219]>, <Hop 10 [142.250.56.129]>, <Hop 20 [64.233.162.147]>]
>>>
PS. works for linux too but returns list with last hop only
Hi,
I came across you're package and was hoping to check multiple servers whether they're up or not.
So, I went for multiping as suggested in the docs. The problem is when the socket opens it gives the error
AttributeError: module 'socket' has no attribute 'IPPROTO_ICMPV6'
I went snooping in the code base, it refers to line 579 in sockets.py
return socket.socket(
family=socket.AF_INET6,
type=type,
proto=socket.IPPROTO_ICMPV6)
I've tried the module with both py 3.7.9 and 3.9.5. Works perfectly fine in 3.9.5 but gives the error in 3.7.9.
I changed the codebase in 3.7.9, with
IPPROTO_ICMP
and it worked.
So my request is if we can amend the current codebase to
try:
return socket.socket(
family=socket.AF_INET6,
type=type,
proto=socket.IPPROTO_ICMPV6)
except AttributeError:
return socket.socket(
family=socket.AF_INET6,
type=type,
proto=socket.IPPROTO_ICMP)
let me know if it is possible. or any help required from my side.
Also, on side, haven't gone through the whole code base but needed some guidance, will be multiping be quicker on roughly 70 servers, or should I think about async socket programming? PS: the client wants the result after every 30 seconds.
Thanks in advance!
Using your library I am having issues with the traceroute portion. Below is my code. I feel like I have practically followed your example besides using input instead of a pre-defined IP.
from icmplib import ping, multiping, traceroute, Host, Hop
def trace(addresses):
for address in addresses:
hops = traceroute(
"{address}", count=3, interval=0.5, timeout=2, max_hops=10, fast_mode=False,
)
print(address)
print("Distance (ttl) Address Average round-trip time")
last_distance = 0
for hop in hops:
if last_distance + 1 != hop.distance:
print("****")
print(f"{hop.distance} {hop.address} {hop.avg_rtt} ms")
last_distance = hop.distance
I am a beginner so maybe I am doing something wrong but it seems like hop isn't defined in your library.
Looking at the is_ipv4_address
method, it seems it uses a somewhat simple method to test for an IPv4 address. Testing 999.999.999.999
would evaluate to True
and can lead to resolve
assuming that the address is valid.
If I may suggest a solution, you could skip the entirety of the manual work and rely solely on socket.getaddrinfo
which will return the address immediately if it does not require resolution:
I'm using
time.time()
here to show whether the resolution involves a DNS server or not, but I did check withtcpdump
.
>>> import socket
>>> import time
>>>
>>> def resolve(host):
... checkpoint = time.time()
... try:
... print(socket.getaddrinfo(host, port=None)[0][4][0])
... except OSError:
... print('failed to resolve')
... print('took', time.time() - checkpoint)
...
>>> resolve('9.9.9.9')
9.9.9.9
took 0.00821995735168457
>>>
>>> resolve ('666.666.666.666')
failed to resolve
took 0.031622886657714844
This will make it so is_ipv4_address
and is_ipv6_address
are no longer required as the tests are performed by socket
.
In addition, there is no way to specify a preferred address family and IPv4 is a forced-defaulted. When an OS has IPv4 and IPv6 routing (i.e. default route over both families), it will prefer IPv6 when calling socket.getaddrinfo
by default as it is the newer version.
As such, I would suggest that the resolve
function have a keyword argument that defaults to None
, but can be set to either 4
or 6
to force a specific address family.
For example:
def resolve(name, address_family=None):
if address_family == 4:
# Force IPv4-only
address_family = socket.AF_INET
elif address_family == 6:
# Force IPv6-only
address_family = socket.AF_INET6
else:
# This lets the OS pick which is best (i.e. IPv6 if it has connectivity)
address_family = None
try:
return socket.getaddrinfo(
host=name,
port=None,
family=address_family,
)[0][4][0]
except OSError:
raise NameLookupError(name)
P.S. Great work on the library
hosts = ['172.20.20.1','172.20.20.2','172.20.20.3',...] # 1173 items in the list
result = await async_multiping(hosts, count=2, timeout=1, interval=0.4, privileged=False, payload_size=16)
for item in result:
pass
------------------------------------------------------------
172.20.20.62
------------------------------------------------------------
Packets sent: 2
Packets received: 2
Packet loss: 0.0%
Round-trip times: 2.74 ms / 410.135 ms / 817.53 ms
Jitter: 814.79 ms
------------------------------------------------------------
172.20.20.80
------------------------------------------------------------
Packets sent: 2
Packets received: 2
Packet loss: 0.0%
Round-trip times: 1.934 ms / 2.99 ms / 4.046 ms
Jitter: 2.113 ms
------------------------------------------------------------
172.20.20.65
------------------------------------------------------------
Packets sent: 0
Packets received: 0
Packet loss: 0.0%
Round-trip times: 0.0 ms / 0.0 ms / 0.0 ms
Jitter: 0.0 ms
------------------------------------------------------------
172.20.20.81
------------------------------------------------------------
Packets sent: 0
Packets received: 0
Packet loss: 0.0%
Round-trip times: 0.0 ms / 0.0 ms / 0.0 ms
Jitter: 0.0 ms
------------------------------------------------------------
172.20.20.63
------------------------------------------------------------
Packets sent: 0
Packets received: 0
Packet loss: 0.0%
Round-trip times: 0.0 ms / 0.0 ms / 0.0 ms
Jitter: 0.0 ms
If there are more than 1024 addresses, then in the answer I also get instances with standard (empty) data.
Or are there options for how this can be handled?
Thanks for bringing up such a sleek library.
This icmplib seems to only support ICMP client mode where the ping is initiated.
It would be better if it could support the ICMP server mode as well.
Cheers.
Hi,
i think it would be nice if you would implement a function which listens for Echo Request packets as ICMPv4Socket.receive only returns Echo Reply packets.
Thanks!
# docker-compose exec home-assistant bash
bash-5.0# /usr/local/bin/python3
Python 3.8.5 (default, Sep 10 2020, 14:23:57)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from icmplib import ping
>>> ping('192.168.1.1')
<Host [192.168.1.1]>
>>> host = ping('192.168.1.1')
>>> host.is_alive
True
>>> host = ping('foo:bar:baz:1::101')
>>> host.is_alive
True
>>> host = ping('only-aaaa.example.org')
>>> host.is_alive
False
bash-5.0# ping6 -c 1 only-aaaa.example.org
PING only-aaaa.example.org (foo:bar:baz:1::101): 56 data bytes
64 bytes from foo:bar:baz:1::101: seq=0 ttl=62 time=0.981 ms
You can probably reproduce the same with ipv6.google.com
:
bash-5.0# host ipv6.google.com
ipv6.google.com is an alias for ipv6.l.google.com.
ipv6.l.google.com has IPv6 address 2a00:1450:400a:800::200e
bash-5.0# ping6 ipv6.google.com
PING ipv6.google.com (2a00:1450:400a:802::200e): 56 data bytes
64 bytes from 2a00:1450:400a:802::200e: seq=0 ttl=111 time=10.882 ms
^C
--- ipv6.google.com ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 10.882/10.882/10.882 ms
bash-5.0# pip list | grep icmplib
icmplib 1.1.3
bash-5.0# python3
Python 3.8.5 (default, Sep 10 2020, 14:23:57)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from icmplib import ping
>>> host = ping('ipv6.google.com')
>>> host.is_alive
False
>>>
Issue originally reported here: home-assistant/core#40232 (comment)
I am running a python script that pings a host in a specific interval.
From time to time (~5 times a day) I get this error:
File "/home/asd/venv/asd/lib/python3.7/site-packages/icmplib/ping.py", line 123, in ping
reply = socket.receive()
File "/home/asd/venv/asd/lib/python3.7/site-packages/icmplib/sockets.py", line 313, in receive
reply_time=reply_time)
File "/home/asd/venv/asd/lib/python3.7/site-packages/icmplib/sockets.py", line 210, in _read_reply
self._config.ICMP_PAYLOAD_OFFSET
struct.error: unpack requires a buffer of 4 bytes
Here is the line calling icmplib:
icmp_ping(SOME_IP, 3)
recvmsg(3, {msg_name={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("192.168.208.1")}, msg_namelen=128->16, msg_iov=[{iov_base="E\0\0T\220\242\0\0@\1\310\256\300\250\320\1\300\250\320\5\0\0a\251 D\0\1\230\ff_"..., iov_len=192}], msg_iovlen=1, msg_control=[{cmsg_len=32, cmsg_level=SOL_SOCKET, cmsg_type=SCM_TIMESTAMP, cmsg_data={tv_sec=1600523416, tv_usec=512848}}], msg_controllen=32, msg_flags=0}, 0) = 84
It looks like the ping binary looks at the cmsg_type
and retrieves the timestamp from cmsg_data
to give a more accurate time.
icmplib currently gets time using time()
which is subject to fluctuations due to cpu load.
Tried launching a ping with the privileged=False
option, but still throws a SocketPermissionError:
>>> ping("8.8.8.8", privileged=False)
Traceback (most recent call last):
File "/home/david/.miniconda3/envs/ping2mqtt/lib/python3.8/site-packages/icmplib/sockets.py", line 88, in __init__
self._sock = self._create_socket(
File "/home/david/.miniconda3/envs/ping2mqtt/lib/python3.8/site-packages/icmplib/sockets.py", line 452, in _create_socket
return socket.socket(
File "/home/david/.miniconda3/envs/ping2mqtt/lib/python3.8/socket.py", line 231, in __init__
_socket.socket.__init__(self, family, type, proto, fileno)
PermissionError: [Errno 13] Permission denied
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/david/.miniconda3/envs/ping2mqtt/lib/python3.8/site-packages/icmplib/ping.py", line 131, in ping
sock = ICMPv4Socket(
File "/home/david/.miniconda3/envs/ping2mqtt/lib/python3.8/site-packages/icmplib/sockets.py", line 97, in __init__
raise SocketPermissionError
icmplib.exceptions.SocketPermissionError: Root privileges are required to create the socket
System details:
Also tried with Python 3.9.4 from within a Docker container, running as root, and when setting the privileged=False
setting, the outcome is the same:
>>> from icmplib import ping
>>> ping("8.8.8.8", privileged=False)
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/icmplib/sockets.py", line 88, in __init__
self._sock = self._create_socket(
File "/usr/local/lib/python3.9/site-packages/icmplib/sockets.py", line 452, in _create_socket
return socket.socket(
File "/usr/local/lib/python3.9/socket.py", line 232, in __init__
_socket.socket.__init__(self, family, type, proto, fileno)
PermissionError: [Errno 13] Permission denied
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.9/site-packages/icmplib/ping.py", line 131, in ping
sock = ICMPv4Socket(
File "/usr/local/lib/python3.9/site-packages/icmplib/sockets.py", line 97, in __init__
raise SocketPermissionError
icmplib.exceptions.SocketPermissionError: Root privileges are required to create the socket
>>> ping("8.8.8.8")
<Host [8.8.8.8]>
Would be a great feature to have the possibilty to set DSCP/TOS bits for ICMP and traceroute.
Thanks in advance.
Hi!
I am having troubles (see home-assistant/core#60781) with the library being used by home-assistant. The device tracker uses the library to ping device to determine if they are available. The problem is, if a name does not resolve, an exception is thrown and no device is marked "reachable" as there is no return at all.
I saw the comment that hostnames are not recommended (unfortunately no further explanation).
Would that mean for the home-assistant integration to first run a hostname resolve to see what is resolvable and then do a ping only for those to avoid exceptions or is there an exception handling missing in the library?
Any help would be appreciated to determine where the issue could be resolved. Thanks!
>>> icmplib.multiping(["ping.lrz.de", "192.168.1.1"])
[<Host [129.187.10.17]>, <Host [192.168.1.1]>]
>>> icmplib.multiping(["ping.lrz.de", "192.168.1.1", "invalid.invalid"])
Traceback (most recent call last):
File "/nix/store/kf6rq5lrpbvz9nivynbrjixz1xi36svb-python3.9-icmplib-3.0.2/lib/python3.9/site-packages/icmplib/utils.py", line 158, in async_resolve
lookup = await loop.getaddrinfo(
File "/nix/store/wq6s8i407ic4qp1dvd5yhrnd0kflzh6x-python3-3.9.12/lib/python3.9/asyncio/base_events.py", line 861, in getaddrinfo
return await self.run_in_executor(
File "/nix/store/wq6s8i407ic4qp1dvd5yhrnd0kflzh6x-python3-3.9.12/lib/python3.9/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
File "/nix/store/wq6s8i407ic4qp1dvd5yhrnd0kflzh6x-python3-3.9.12/lib/python3.9/socket.py", line 954, in getaddrinfo
for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno -2] Name or service not known
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/nix/store/kf6rq5lrpbvz9nivynbrjixz1xi36svb-python3.9-icmplib-3.0.2/lib/python3.9/site-packages/icmplib/multiping.py", line 267, in multiping
return asyncio.run(
File "/nix/store/wq6s8i407ic4qp1dvd5yhrnd0kflzh6x-python3-3.9.12/lib/python3.9/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/nix/store/wq6s8i407ic4qp1dvd5yhrnd0kflzh6x-python3-3.9.12/lib/python3.9/asyncio/base_events.py", line 647, in run_until_complete
return future.result()
File "/nix/store/kf6rq5lrpbvz9nivynbrjixz1xi36svb-python3.9-icmplib-3.0.2/lib/python3.9/site-packages/icmplib/multiping.py", line 163, in async_multiping
return [task.result() for task in tasks]
File "/nix/store/kf6rq5lrpbvz9nivynbrjixz1xi36svb-python3.9-icmplib-3.0.2/lib/python3.9/site-packages/icmplib/multiping.py", line 163, in <listcomp>
return [task.result() for task in tasks]
File "/nix/store/kf6rq5lrpbvz9nivynbrjixz1xi36svb-python3.9-icmplib-3.0.2/lib/python3.9/site-packages/icmplib/ping.py", line 263, in async_ping
address = (await async_resolve(address, family))[0]
File "/nix/store/kf6rq5lrpbvz9nivynbrjixz1xi36svb-python3.9-icmplib-3.0.2/lib/python3.9/site-packages/icmplib/utils.py", line 168, in async_resolve
return await async_resolve(name, 6)
icmplib.exceptions.NameLookupError: The name 'invalid.invalid' cannot be resolved
My hope would be that even if one of the names cannot be looked up, a result would be returned for the others, and potentially that a result indicating the lookup failure would be given back for the unresolveable name.
I notice that both high level ping
/multiping
and low level ICMPReply
do not contain ttl information, which is useful.
And I think it is easy to retrive the ttl in ICMPv4Socket
Please add async version of traceroute.
Feature Request - Add Source parameter to select an Interface to ping from.
Hi,
when i use traceroute module, the last address not my address in output, does it mean the address can't reach ?
And Hop 3 to Hop 21 means 4~20 not response ?
Python 3.8.5 (default, Sep 22 2020, 23:34:13)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-36)] on linux
Type "help", "copyright", "credits" or "license" for more information.
from icmplib import *
traceroute("192.168.46.25")
[<Hop 1 [193.22.152.1]>, <Hop 2 [172.16.0.11]>, <Hop 3 [192.168.101.42]>, <Hop 21 [38.142.238.113]>]
I'm using debian 10 with python3 3.7.3 and pinging doesn't work without being root. This is what I've tried:
Package version is 2.0.1
Python 3.7.3 (default, Jul 25 2020, 13:03:44)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from icmplib import ping
>>> test = ping("8.8.8.8", count=1, interval=1, timeout=1, privileged=False)
Traceback (most recent call last):
File "/home/max/.local/lib/python3.7/site-packages/icmplib/sockets.py", line 88, in __init__
socket.SOCK_DGRAM)
File "/home/max/.local/lib/python3.7/site-packages/icmplib/sockets.py", line 448, in _create_socket
proto=socket.IPPROTO_ICMP)
File "/usr/lib/python3.7/socket.py", line 151, in __init__
_socket.socket.__init__(self, family, type, proto, fileno)
PermissionError: [Errno 13] Permission denied
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/max/.local/lib/python3.7/site-packages/icmplib/ping.py", line 133, in ping
privileged=privileged)
File "/home/max/.local/lib/python3.7/site-packages/icmplib/sockets.py", line 95, in __init__
raise SocketPermissionError
icmplib.exceptions.SocketPermissionError: Root privileges are required to create the socket
>>>
Edit: It works on my other ubuntu server with python 3.8.5.
When I test the multiping function, I get the result below:
xxx rtt: min[-4411.397] avg[-4411.397] max[0.0] lose_packet:0.5 packets_received:1
xxx rtt: min[-4625.375] avg[-3237.526] max[0.0] lose_packet:0.0 packets_received:2
Test Case like below:
network = '192.168.10.0/24'
net = ipaddress.ip_network(network)
hosts = []
for ip in net:
hosts.append(str(ip))
res = multiping(hosts, count=2, timeout=1)
alive_host = []
for ip in res:
if ip.is_alive:
alive_host.append((ip.address, ip.avg_rtt))
if ip.max_rtt < 0 or ip.min_rtt < 0 or ip.avg_rtt < 0:
print("{} rtt: min[{}] avg[{}] max[{}] lose_packet:{} packets_received:{}".format(ip.address, ip.min_rtt, ip.avg_rtt, ip.max_rtt, ip.packet_loss, ip.packets_received))
I was wondering if it is possible to ping a specified port number when do this?
I was using the source parameter on icmplib.ping with the intention of sending a ping request via different interfaces to the same destination. With Linux command line ping you can specify the -I parameter to be either a interface name or an IP address and the first option can be used to get the desired effect, but one can't do this with icmplib.ping. If one merely specifies the desired source address then the ping goes out whatever interface is the default route for the target address which wont be appropriate. It is of course possible to circumvent the problem by matching the source address with an ip rule and directing it to the appropriate interface but this does start to build in dependencies on whatever ip address the interface happens to have and leads to unnecessary complication. Any chance of an option to specify the interface required?
Current implementation does not support zone index of IPv6 addresses (...%if
part to designate interface). Here, I try to ping fe80:aaaa:bbbb:cccc:dddd%wlo1
, which clearly exists (it is my own interface's address on wifi).
Using the standard ping
command, no problem:
$ ping -c2 fe80::aaaa:bbbb:cccc:dddd%wlo1
PING fe80::aaaa:bbbb:cccc:dddd%wlo1(fe80::aaaa:bbbb:cccc:dddd%wlo1) 56 octets de données
64 octets de fe80::aaaa:bbbb:cccc:dddd%wlo1 : icmp_seq=1 ttl=64 temps=0.026 ms
64 octets de fe80::aaaa:bbbb:cccc:dddd%wlo1 : icmp_seq=2 ttl=64 temps=0.026 ms
--- statistiques ping fe80::aaaa:bbbb:cccc:dddd%wlo1 ---
2 paquets transmis, 2 reçus, 0% packet loss, time 1017ms
rtt min/avg/max/mdev = 0.026/0.026/0.026/0.000 ms
Using icmplib
:
In [1]: import icmplib
...: import socket
In [2]: s = icmplib.ICMPv6Socket(privileged=False)
...: request = icmplib.ICMPRequest("fe80::aaaa:bbbb:cccc:dddd%wlo1", 0, 4)
...: s.send(request)
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
~/.local/lib/python3.9/site-packages/icmplib/sockets.py in send(self, request)
271 request._time = time()
--> 272 self._sock.sendto(packet, (request.destination, 0))
273
OSError: [Errno 22] Invalid argument
During handling of the above exception, another exception occurred:
ICMPSocketError Traceback (most recent call last)
<ipython-input-2-daa259b33195> in <module>
1 s = icmplib.ICMPv6Socket(privileged=False)
2 request = icmplib.ICMPRequest("fe80::aaaa:bbbb:cccc:dddd%wlo1", 0, 4)
----> 3 s.send(request)
~/.local/lib/python3.9/site-packages/icmplib/sockets.py in send(self, request)
283
284 except OSError as err:
--> 285 raise ICMPSocketError(str(err))
286
287 def receive(self, request=None, timeout=2):
ICMPSocketError: [Errno 22] Invalid argument
Basically, s.send
forward the request's destination to socket.socket.sendto
, in a tuple (address, port/identifier). There, the …%wlo1
part is not allowed:
In [3]: packet = s._create_packet(id=request.id, sequence=request.sequence, payload=b"")
...: s._sock.sendto(packet, ("fe80::aaaa:bbbb:cccc:dddd%wlo1", 0))
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
<ipython-input-3-b1659f9707d4> in <module>
1 packet = s._create_packet(id=request.id, sequence=request.sequence, payload=b"")
----> 2 s._sock.sendto(packet, ("fe80::aaaa:bbbb:cccc:dddd%wlo1", 0))
OSError: [Errno 22] Invalid argument
One may use socket.getaddrinfo
to get the correct tuple to forward (here, "3" is the number of my wlo1 interface):
In [4]: dest = socket.getaddrinfo("fe80::aaaa:bbbb:cccc:dddd%wlo1", port=None, family=socket.AF_INET6, type=socket.SOCK_DGRAM)[0][4]
...: print(dest)
...: s._sock.sendto(packet, dest)
...: s.receive()
('fe80::aaaa:bbbb:cccc:dddd', 0, 0, 3)
Out[4]: <ICMPReply [fe80::aaaa:bbbb:cccc:dddd]>
Hi, when I try traceroute to any IPv6 address, this lib just put the dst as the only hop.
I have turnoff the firewall on this machine.
Command:
Python 3.6.8 (default, Nov 16 2020, 16:55:22)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import icmplib
>>> hops = icmplib.traceroute('2001:470:1:18::115')
>>> print(hops)
[<Hop 1 [2001:470:1:18::115]>]
TCPDUMP:
07:38:11.602563 IP6 (hlim 64, next-header ICMPv6 (58) payload length: 64) MY_LOCAL_IPV6 > 2001:470:1:18::115: [icmp6 sum ok] ICMP6, echo request, seq 0
07:38:11.662401 IP6 (flowlabel 0xe3c64, hlim 57, next-header ICMPv6 (58) payload length: 64) 2001:470:1:18::115 > MY_LOCAL_IPV6 [icmp6 sum ok] ICMP6, echo reply, seq 0
07:38:11.662839 IP6 (hlim 64, next-header ICMPv6 (58) payload length: 64) MY_LOCAL_IPV6 > 2001:470:1:18::115: [icmp6 sum ok] ICMP6, echo request, seq 1
07:38:11.722478 IP6 (flowlabel 0xe3c64, hlim 57, next-header ICMPv6 (58) payload length: 64) 2001:470:1:18::115 > MY_LOCAL_IPV6 [icmp6 sum ok] ICMP6, echo reply, seq 1
traceroute using linux tools
[root@us-iad03-t2-1g-1 ~]# traceroute 2001:470:1:18::115
traceroute to 2001:470:1:18::115 (2001:470:1:18::115), 30 hops max, 80 byte packets
1 po001.c25.iad03.us.misaka.io (2a0e:6902:2000::4) 0.275 ms 0.105 ms 0.096 ms
2 v204.cs01.iad03.us.misaka.io (2a0e:6902:2000::2) 0.289 ms v204.cs02.iad03.us.misaka.io (2a0e:6902:2000::3) 0.287 ms 0.191 ms
3 v204.cs02.iad03.us.misaka.io (2a0e:6902:2000::3) 0.182 ms 2001:504:31::1b1b:1 (2001:504:31::1b1b:1) 4.288 ms 7.232 ms
4 2001:504:31::1b1b:1 (2001:504:31::1b1b:1) 10.159 ms 13.229 ms 16.196 ms
5 100ge14-1.core2.ash1.he.net (2001:470:0:4c4::1) 0.578 ms 100ge1-2.core1.ash1.he.net (2001:470:0:277::1) 22.035 ms 100ge14-1.core2.ash1.he.net (2001:470:0:4c4::1) 0.393 ms
6 100ge1-2.core1.ash1.he.net (2001:470:0:277::1) 0.591 ms 0.832 ms 100ge7-2.core1.pao1.he.net (2001:470:0:8f::1) 59.183 ms
7 100ge14-1.core3.fmt1.he.net (2001:470:0:30::1) 72.677 ms 72.549 ms 72.433 ms
8 100ge14-1.core3.fmt1.he.net (2001:470:0:30::1) 72.324 ms 72.216 ms 67.062 ms
9 2001:470:1:18::115 (2001:470:1:18::115) 59.648 ms 59.562 ms 59.686 ms
It would be better to use monotonic time to calculate timeout in IO logic.
How may host can we ping using multi ping. I tried with 1000 host. Ping was successful but CPU utilisation was more than 100%?
Is there a known issue?
There seems to be a problem with matching incoming responses to requests.
If I
I get a reported round trip time of 5 milliseconds for the second request, even if that probably will take longer.
There should be some kind of mechanism to validate that any response receives actually matches the current request so that previous answers don't throw off the results.
Hi,
First, thank you. I am enjoying this module immensely! I love that you provide the privilege option. for some of us who have to work in locked down environments, this is key.
I have just updated my ping check script to use icmplib!
You recommend not using FQDNs. May I as why? Most of my inventory files use FQDNs which is why I ask.
This is not an issue but more of a feature request.
Would it be possible to add reverse lookup (get FQDN from IP address)?
In the traceroute module "avg_rtt" does not return the average but the total round trip.
Looking at how its implemented for the ping module, the following is missing:
if packets_received:
avg_rtt /= packets_received
I'll be happy to create a PR if you want, though it's a simple fix.
PyPi: version 3.0
GitHub: version 3.0.0
They should be the same
Hi!
I was porting another ICMP
package over to be async when I found this. I would love to create a async def ping
which is not blocking and is awaitable, using the current codebase but running the sockets in executors, and using a randint instead of PID.
Let me know if you are open to accept PRs.
The alternative is for me to create my own package that basically only utilizes yours, but has it's own async def ping
function.
Hi Valentin,
first of all, thank you very much for this nice library. It is exactly, what I am looking for! Yet, I have two small issues... perhaps you can enlighten me.
The first one is the privileged parameter. Please see the error message below... whether I set it to True or False, does not matter -- I get this error.
a = ping("10.10.10.10", count=3, interval=1, timeout=2, privileged=True) File "/home/fl/SW/miniconda/envs/mib/lib/python3.9/site-packages/icmplib/ping.py", line 141, in ping with _Socket(source, privileged) as sock: File "/home/fl/SW/miniconda/envs/mib/lib/python3.9/site-packages/icmplib/sockets.py", line 97, in __init__ raise SocketPermissionError icmplib.exceptions.SocketPermissionError: Root privileges are required to create the socket
I attempted to use icmplib in an anaconda environment with up-to-date packages. The only thing a little unusual is that it is started from inside a thread. Multiping also does not work here, it exits like this:
RuntimeError: can't register atexit after shutdown Task exception was never retrieved future: <Task finished name='Task-11' coro=<async_ping() done, defined at /home/fl/SW/miniconda/envs/mib/lib/python3.9/site-packages/icmplib/ping.py:168> exception=RuntimeError("can't register atexit after shutdown")> Traceback (most recent call last): File "/home/fl/SW/miniconda/envs/mib/lib/python3.9/site-packages/icmplib/ping.py", line 263, in async_ping address = (await async_resolve(address, family))[0] File "/home/fl/SW/miniconda/envs/mib/lib/python3.9/site-packages/icmplib/utils.py", line 158, in async_resolve lookup = await loop.getaddrinfo( File "/home/fl/SW/miniconda/envs/mib/lib/python3.9/asyncio/base_events.py", line 856, in getaddrinfo return await self.run_in_executor( File "/home/fl/SW/miniconda/envs/mib/lib/python3.9/asyncio/base_events.py", line 809, in run_in_executor executor = concurrent.futures.ThreadPoolExecutor( File "/home/fl/SW/miniconda/envs/mib/lib/python3.9/concurrent/futures/__init__.py", line 49, in __getattr__ from .thread import ThreadPoolExecutor as te File "/home/fl/SW/miniconda/envs/mib/lib/python3.9/concurrent/futures/thread.py", line 37, in <module> threading._register_atexit(_python_exit) File "/home/fl/SW/miniconda/envs/mib/lib/python3.9/threading.py", line 1374, in _register_atexit raise RuntimeError("can't register atexit after shutdown") RuntimeError: can't register atexit after shutdown
But multiping is not that important to me and I just wanted to let you know.
Thanks for your help!
Cheers,
Frank
First of all, I have to say your library is cool. But I'm using this multiping function. Is there anyway to get TTL value here?
I propose to implement a callback for the 'multiping' functions, to perform some functions after receiving a response from the host
Using your library as an example
import asyncio
from datetime import datetime
from icmplib import Host, ICMPLibError, ICMPRequest, AsyncSocket, ICMPv4Socket, ICMPv6Socket
from icmplib.utils import unique_identifier, is_ipv6_address, async_resolve, is_hostname
from test_funcs import getHosts
async def async_ping(address, count=4, interval=1, timeout=2, id=None,
source=None, family=None, privileged=True, callback_func=None, **kwargs):
if is_hostname(address):
address = (await async_resolve(address, family))[0]
if is_ipv6_address(address):
_Socket = ICMPv6Socket
else:
_Socket = ICMPv4Socket
id = id or unique_identifier()
packets_sent = 0
rtts = []
with AsyncSocket(_Socket(source, privileged)) as sock:
for sequence in range(count):
if sequence > 0:
await asyncio.sleep(interval)
request = ICMPRequest(
destination=address,
id=id,
sequence=sequence,
**kwargs)
try:
sock.send(request)
packets_sent += 1
reply = await sock.receive(request, timeout)
reply.raise_for_status()
rtt = (reply.time - request.time) * 1000
rtts.append(rtt)
except ICMPLibError:
pass
if callback_func:
return await callback_func(Host(address, packets_sent, rtts))
return Host(address, packets_sent, rtts)
async def async_multiping(addresses, count=2, interval=0.5, timeout=2,
concurrent_tasks=50, source=None, family=None, privileged=True,
callback_func=None, **kwargs):
loop = asyncio.get_running_loop()
tasks = []
tasks_pending = set()
for address in addresses:
if len(tasks_pending) >= concurrent_tasks:
_, tasks_pending = await asyncio.wait(
tasks_pending,
return_when=asyncio.FIRST_COMPLETED)
task = loop.create_task(
async_ping(
address=address,
count=count,
interval=interval,
timeout=timeout,
source=source,
family=family,
privileged=privileged,
callback_func=callback_func,
**kwargs)
)
tasks.append(task)
tasks_pending.add(task)
await asyncio.wait(tasks_pending)
return [task.result() for task in tasks]
async def callback(host):
print(host.address)
# send_me(host) example func
return host
async def test():
startTime = datetime.now()
hosts = await getHosts()
hostsDown = []
hostsUp = []
for result in await async_multiping(
hosts, count=1, timeout=1, interval=0.4, privileged=False, payload_size=8, callback_func=callback
):
if result.is_alive:
hostsUp.append(result)
else:
hostsDown.append(result)
exTime = (datetime.now() - startTime).total_seconds()
print(' -', len(hostsUp), 'hosts up out of', len(hosts))
print(' -', 'execution speed', exTime, 'secs')
asyncio.run(test())
Output
172.20.3.12
172.20.3.198
172.20.3.97
172.20.4.136
172.20.4.184
- 1160 hosts up out of 1173
- execution speed 1.371824 secs
In icmplib/sockets.py
, the response buffer allocated by the receive
method isn't big enough, when the echo request payload_size
is greater than 996
bytes.
response = self._sock.recvfrom(1024)
Using examples/ping.py
for testing:
On Ubuntu (root), the calculated bytes_received
value is incorrect, as response
is truncated.
On Windows 10, the self._sock.recvfrom(1024)
triggers the below exception, which is swallowed within the icmplib/ping.py
ping
method by:
except ICMPLibError:
pass
ICMPLibError: [WinError 10040] A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself
The flow on effect is that RTTs aren't calculated, and the host.is_alive
is incorrectly set to False
,
Simply allocating a buffer that can hold the max possible ICMP packet size (i.e. IPv4 max packet size) seems to work:
response = self._sock.recvfrom(65535)
(Windows), sending 3 pings:
(venv_icmplib) D:\icmplib\examples>python ping.py
ICMP payload size: 996
host.address: 10.0.0.2
host.min_rtt: 1.0
host.avg_rtt: 1.0
host.max_rtt: 1.279
host.rtts: [1.2786388397216797, 0.9996891021728516, 0.9996891021728516]
host.packets_sent: 3
host.packets_received: 3
host.packet_loss 0.0
host.jitter: 0.139
host.is_alive: True
(venv_icmplib) D:\icmplib\examples>python ping.py
ICMP payload size: 997
host.address: 10.0.0.2
host.min_rtt: 0.0
host.avg_rtt: 0.0
host.max_rtt: 0.0
host.rtts: []
host.packets_sent: 3
host.packets_received: 0
host.packet_loss 1.0
host.jitter: 0.0
host.is_alive: False
This is a nice tool, hope this helps!
I have some packets as bytes
and want to analyse them. I also want to generate them as bytes
. Currently it is done within the classes for sockets, but they are tied to network functions.
It is proposed to move the parsing and serialization code into .models
and add the 2 methods, __bytes__
and from_bytes
.
I'm planning to include this on my flask api project something like http://url/icmplib/ping/192.168.1.1 and will give a response of json.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.