Codebase list python-icmplib / 78ba80e
Import upstream version 3.0.2 Kali Janitor 2 years ago
27 changed file(s) with 3757 addition(s) and 1737 deletion(s). Raw diff Collapse all Expand all
00 # Changelog
11
22 All notable changes to this project will be documented in this file.
3
4 ## [v3.0.2](https://github.com/ValentinBELYN/icmplib/releases/tag/v3.0.2) - 2021-10-31
5 - Add support for IPv6 addresses with zone index.
6 - The `payload` property of an `ICMPRequest` now returns a random value if the payload is not defined instead of `None`.
7 - Optimize imports.
8
9 ## [v3.0.1](https://github.com/ValentinBELYN/icmplib/releases/tag/v3.0.1) - 2021-08-14
10 - Make the `SocketPermissionError` more explicit when the OS does not allow instantiation of unprivileged sockets.
11 - Delete unnecessary properties from exceptions.
12 - Improve SEO on GitHub.
13
14 ## [v3.0.0](https://github.com/ValentinBELYN/icmplib/releases/tag/v3.0.0) - 2021-06-01
15 **icmplib 3.0 is here! :rocket:**
16
17 - The library is now asynchronous!
18 - Introduce new functions: `async_ping`, `async_multiping` and `async_resolve`.
19 - Add a new `AsyncSocket` class to make an ICMP socket asynchronous.
20 - Rewrite models:
21 - All the properties of `Host` and `Hop` classes are now lazy.
22 - Add new properties to the `Host` and `Hop` classes: you can retrieve the list of round-trip times and calculate the jitter.
23 - Add the metaclass `__str__` to visualize the results more easily.
24 - Rewrite the `ping`, `multiping` and `traceroute` functions:
25 - The identifier of ICMP requests is now handled automatically by the library.
26 - Add the ability to set the address family if a hostname or an FQDN is specified.
27 - A new implementation is used for the `multiping` function. It should be more reliable.
28 - The `multiping` function is now in a dedicated module.
29 - Rewrite the documentation to help you get started with icmplib.
30 - Improve the `resolve` function to return all IP addresses found.
31 - Add the `is_hostname` function to check if a string is a hostname or an FQDN.
32 - Delete the experimental class `BufferedSocket`, replaced by `AsyncSocket`.
33 - Performance and compatibility improvement.
34 - Many other small changes and fixes.
35
36 Special thanks to [@JonasKs](https://github.com/JonasKs) and [@bdraco](https://github.com/bdraco) for their suggestions, advice and feedback!
37
38 > Python 3.7 or later is now required.
39
40 ## [v2.1.1](https://github.com/ValentinBELYN/icmplib/releases/tag/v2.1.1) - 2021-03-21
41 - :bug: Revert changes made to the `traceroute` function due to a bug.
42
43 > This version is the last of the 2.x branch. See you soon for the release of icmplib 3.0!
44
45 ## [v2.1.0](https://github.com/ValentinBELYN/icmplib/releases/tag/v2.1.0) - 2021-03-20
46 - Add a `family` parameter to the `resolve` function to define the address family.
47 - Improve the reliability of the results of the `traceroute` function.
48
49 ## [v2.0.2](https://github.com/ValentinBELYN/icmplib/releases/tag/v2.0.2) - 2021-02-07
50 - Rename the default branch from `master` to `main`.
51 - Add more details about the `privileged` parameter in the `README` file (part 2).
52 - :bug: Fix a bug preventing the `traceroute` function to work with IPv6 addresses (part 2).
53
54 ## [v2.0.1](https://github.com/ValentinBELYN/icmplib/releases/tag/v2.0.1) - 2020-12-12
55 - Handle `EACCES` errors at sockets level.
56 - Add some details about the `privileged` parameter in the `README` file.
57 - :bug: Fix a bug preventing the `traceroute` function to work with IPv6 addresses.
58
59 ## [v2.0.0](https://github.com/ValentinBELYN/icmplib/releases/tag/v2.0.0) - 2020-11-15
60 **icmplib 2.0 is here! :rocket:**
61
62 - New library architecture.
63 - Add a new `multiping` function. This function will be faster and more memory efficient.
64 - Add the ability to use the library without root privileges.
65 - Add the ability to set a source IP address for sending your ICMP packets.
66 - Add a `first_hop` parameter to the `traceroute` function to set the initial time to live value ([@scoulondre](https://github.com/scoulondre)).
67 - Add two new exceptions:
68 - `NameLookupError`: raised when the requested name does not exist or cannot be resolved.
69 - `SocketAddressError`: raised when the requested address cannot be assigned to the socket.
70 - Add a new `BufferedSocket` class (experimental) to send several packets simultaneously without waiting for a response.
71 - The `receive` method of sockets can receive all incoming packets.
72 - Throw new exceptions when instantiating sockets, sending and receiving packets.
73 - Improve the `resolve` function:
74 - A `NameLookupError` is now raised if the requested name does not exist or cannot be resolved.
75 - Improve compatibility with Linux, macOS and Windows.
76 - Delete deprecated properties.
77 - Update docstrings, examples and documentation.
78
79 > Compatibility with existing programs is maintained.
380
481 ## [v1.2.2](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.2.2) - 2020-10-10
582 - Add support for hostnames and FQDN resolution to IPv6 addresses.
683 - Performance improvement.
784
885 ## [v1.2.1](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.2.1) - 2020-09-26
9 - Fix an issue in the `traceroute` function which gave the wrong value for the `avg_rtt` property.
10 - Some other tweaks to the `traceroute` function.
86 - :bug: Fix an issue in the `traceroute` function which gave the wrong value for the `avg_rtt` property ([@patrickfnielsen](https://github.com/patrickfnielsen)).
1187
1288 ## [v1.2.0](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.2.0) - 2020-09-12
1389 - Add the ability to modify the traffic class of ICMP packets.
1591 - Add a new exception `SocketUnavailableError` when an action is performed while a socket is closed.
1692 - Add a warning message on deprecated properties.
1793 - Explicit closure of sockets on built-in functions.
18 - Fix a bug when ICMP responses are not correctly formatted (part 2).
94 - :bug: Fix a bug when ICMP responses are not correctly formatted (part 2).
1995
2096 ## [v1.1.3](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.1.3) - 2020-09-03
21 - Fix a bug when ICMP responses are not correctly formatted.
97 - :bug: Fix a bug when ICMP responses are not correctly formatted.
2298
2399 ## [v1.1.2](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.1.2) - 2020-08-29
24 - Fix a compatibility issue.
100 - :bug: Fix a compatibility issue.
25101
26102 ## [v1.1.1](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.1.1) - 2020-07-10
27 - Fix a bug when the source host does not have an IP address.
103 - :bug: Fix a bug when the source host does not have an IP address.
28104
29105 ## [v1.1.0](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.1.0) - 2020-06-25
106 - Add support for odd size payloads.
30107 - Normalize the names of variables and properties:
31108 - `ICMPReply` class: the `received_bytes` property is deprecated. Use `bytes_received` instead.
32109 - `Host` and `Hop` classes: the `transmitted_packets` property is deprecated. Use `packets_sent` instead.
33110 - `Host` and `Hop` classes: the `received_packets` property is deprecated. Use `packets_received` instead.
34111 - Normalize docstrings.
35 - Add support for odd size payloads.
36112 - Optimizations.
37113
38114 ## [v1.0.4](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.0.4) - 2020-06-14
39115 - Add the `is_closed` property to the `ICMPSocket` class.
40116 - Round round-trip time values by default.
41 - Fix a bug in the `multiping` function: the `id` parameter was ignored.
42 - Fix a bug in the `ICMPSocket` class when instantiated without root privileges.
43117 - Add an index for examples.
118 - :bug: Fix a bug in the `multiping` function where the `id` parameter was ignored.
119 - :bug: Fix a bug in the `ICMPSocket` class when instantiated without root privileges.
44120
45121 ## [v1.0.3](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.0.3) - 2020-05-09
46122 - Add the ability to customize the payload.
47 - Improvements of `ping` and `multiping` functions. It is now possible to pass arguments to the `ICMPRequest` object using keywords arguments `**kwargs`.
123 - Improve the `ping` and `multiping` functions:
124 - You can pass arguments to the `ICMPRequest` object using keywords arguments `**kwargs`.
48125 - Update some docstrings.
49126 - Add new examples.
50127
00 <div align="center">
1 <br>
12 <br>
23 <img src="media/icmplib-logo.png" height="125" width="100" alt="icmplib">
34 <br>
5
46 <br>
5
6 <p><strong>Easily forge ICMP packets and make your own ping and traceroute.</strong></p>
7 <a href="https://pypi.org/project/icmplib">
8 <img src="https://img.shields.io/pypi/dm/icmplib.svg?style=flat-square&labelColor=0366d6&color=005cc5" alt="statistics">
9 </a>
10 <br>
11 <br>
12
137 <div>
148 <a href="#features">Features</a>&nbsp;&nbsp;&nbsp;
159 <a href="#installation">Installation</a>&nbsp;&nbsp;&nbsp;
16 <a href="#built-in-functions">Built-in functions</a>&nbsp;&nbsp;&nbsp;
17 <a href="#icmp-sockets">ICMP sockets</a>&nbsp;&nbsp;&nbsp;
18 <a href="#faq">FAQ</a>&nbsp;&nbsp;&nbsp;
10 <a href="#getting-started">Getting started</a>&nbsp;&nbsp;&nbsp;
11 <a href="#documentation">Documentation</a>&nbsp;&nbsp;&nbsp;
1912 <a href="#contributing">Contributing</a>&nbsp;&nbsp;&nbsp;
2013 <a href="#donate">Donate</a>&nbsp;&nbsp;&nbsp;
2114 <a href="#license">License</a>
2215 </div>
2316 <br>
24 <br>
2517
2618 <pre>icmplib is a brand new and modern implementation of the ICMP protocol in Python.
2719 Use the built-in functions or build your own, you have the choice!
2820
29 <strong>Root privileges are required to use this library (raw sockets).</strong></pre>
21 <a href="CHANGELOG.md">icmplib 3.0 has been released! See what's new 🎉</a></pre>
22
23 <a href="https://pypi.org/project/icmplib">
24 <img src="https://img.shields.io/pypi/dm/icmplib.svg?style=flat-square&labelColor=0366d6&color=005cc5" alt="statistics">
25 </a>
3026 </div>
3127
3228 <br>
3329
3430 ## Features
3531
36 - :deciduous_tree: **Ready-to-use:** icmplib offers ready-to-use functions such as the most popular ones: `ping`, `multiping` and `traceroute`.
37 - :gem: **Modern:** This library uses the latest technologies offered by Python 3.6+ and is fully object-oriented.
38 - :rocket: **Fast:** Each class and function has been designed and optimized to deliver the best performance. Some functions are also multithreaded (like the `multiping` function). You can ping the world in seconds!
39 - :nut_and_bolt: **Powerful and evolutive:** Easily build your own classes and functions with `ICMPv4` and `ICMPv6` sockets.
40 - :fire: **Seamless integration of IPv6:** Use IPv6 the same way you use IPv4. Automatic detection is done without impacting performance.
41 - :rainbow: **Broadcast support** (you must use the `ICMPv4Socket` class to enable it).
42 - :beer: **Support of all operating systems.** Tested on Linux, macOS and Windows.
43 - :metal: **No dependency:** icmplib is a pure Python implementation of the ICMP protocol. It does not use any external dependencies.
32 - :deciduous_tree: **Ready-to-use:** icmplib offers ready-to-use functions such as the most popular ones: `ping`, `multiping` and `traceroute`. An extensive documentation also helps you get started.
33 - :gem: **Modern:** This library uses the latest mechanisms offered by Python 3.6/3.7+ and is fully object-oriented.
34 - :rocket: **Fast:** Each class and function has been designed and optimized to deliver the best performance. Some functions are also asynchronous like the `async_ping` and `async_multiping` functions. You can ping the world in seconds!
35 - :zap: **Powerful:** Use the library without root privileges, set the traffic class of ICMP packets, customize their payload, send broadcast requests and more!
36 - :nut_and_bolt: **Evolutive:** Easily build your own classes and functions with `ICMPv4` and `ICMPv6` sockets.
37 - :fire: **Seamless integration of IPv6:** Use IPv6 the same way you use IPv4.
38 - :beer: **Cross-platform:** Optimized for Linux, macOS and Windows. The library automatically manages the specificities of each system.
39 - :metal: **No dependency:** icmplib is a pure Python implementation of the ICMP protocol. It does not rely on any external dependency.
4440
4541 <br>
4642
4743 ## Installation
4844
49 Install, upgrade and uninstall icmplib with these commands:
50
51 ```shell
52 $ pip3 install icmplib
53 $ pip3 install --upgrade icmplib
54 $ pip3 uninstall icmplib
55 ```
56
57 icmplib requires Python 3.6 or later.
58
59 Import icmplib into your project (only import what you need):
60
61 ```python
62 # For simple use
63 from icmplib import ping, multiping, traceroute, Host, Hop
64
65 # For advanced use (sockets)
66 from icmplib import ICMPv4Socket, ICMPv6Socket, ICMPRequest, ICMPReply
67
68 # Exceptions
69 from icmplib import ICMPLibError, ICMPSocketError, SocketPermissionError
70 from icmplib import SocketUnavailableError, SocketBroadcastError, TimeoutExceeded
71 from icmplib import ICMPError, DestinationUnreachable, TimeExceeded
72 ```
73
74 <br>
75
76 ## Built-in functions
77
78 ### Ping
79 Send *ICMP ECHO_REQUEST* packets to a network host.
80
81 #### Definition
82 ```python
83 ping(address, count=4, interval=1, timeout=2, id=PID, **kwargs)
45 - **Install icmplib**
46
47 The recommended way to install icmplib is to use `pip3`:
48
49 ```shell
50 $ pip3 install icmplib
51 ```
52
53 *Since icmplib 3, Python 3.7 or later is required to use the library.*<br>
54 *If you are using Python 3.6 and you cannot update it, you can still install icmplib 2.*
55
56 - **Import basic functions**
57
58 ```python
59 from icmplib import ping, multiping, traceroute, resolve
60 ```
61
62 - **Import asynchronous functions**
63
64 ```python
65 from icmplib import async_ping, async_multiping, async_resolve
66 ```
67
68 - **Import sockets (advanced)**
69
70 ```python
71 from icmplib import ICMPv4Socket, ICMPv6Socket, AsyncSocket, ICMPRequest, ICMPReply
72 ```
73
74 - **Import exceptions**
75
76 ```python
77 from icmplib import ICMPLibError, NameLookupError, ICMPSocketError
78 from icmplib import SocketAddressError, SocketPermissionError
79 from icmplib import SocketUnavailableError, SocketBroadcastError, TimeoutExceeded
80 from icmplib import ICMPError, DestinationUnreachable, TimeExceeded
81 ```
82
83 *Import only what you need.*
84
85 <br>
86
87 ## Getting started
88
89 ### ping
90
91 Send ICMP Echo Request packets to a network host.
92
93 ```python
94 ping(address, count=4, interval=1, timeout=2, id=None, source=None, family=None, privileged=True, **kwargs)
8495 ```
8596
8697 #### Parameters
98
8799 - `address`
88100
89 The IP address of the gateway or host to which the message should be sent.
101 The IP address, hostname or FQDN of the host to which messages should be sent. For deterministic behavior, prefer to use an IP address.
90102
91103 - Type: `str`
92104
113125
114126 - `id`
115127
116 The identifier of the request. Used to match the reply with the request.<br>
117 In practice, a unique identifier is used for every ping process.
118
119 - Type: `int`
120 - Default: `PID`
121
122 - `**kwargs`
123
124 `Optional` Advanced use: arguments passed to `ICMPRequest` objects.
128 The identifier of ICMP requests. Used to match the responses with requests. In practice, a unique identifier should be used for every ping process. On Linux, this identifier is ignored when the `privileged` parameter is disabled. The library handles this identifier itself by default.
129
130 - Type: `int`
131 - Default: `None`
132
133 - `source`
134
135 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination.
136
137 - Type: `str`
138 - Default: `None`
139
140 - `family`
141
142 The address family if a hostname or FQDN is specified. Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default, this function searches for IPv4 addresses first before searching for IPv6 addresses.
143
144 - Type: `int`
145 - Default: `None`
146
147 - `privileged`
148
149 When this option is enabled, this library fully manages the exchanges and the structure of ICMP packets. Disable this option if you want to use this function without root privileges and let the kernel handle ICMP headers.
150
151 [Learn more about the `privileged` parameter.]
152
153 *Only available on Unix systems. Ignored on Windows.*
154
155 - Type: `bool`
156 - Default: `True`
157
158 - `payload`
159
160 The payload content in bytes. A random payload is used by default.
161
162 - Type: `bytes`
163 - Default: `None`
164
165 - `payload_size`
166
167 The payload size. Ignored when the `payload` parameter is set.
168
169 - Type: `int`
170 - Default: `56`
171
172 - `traffic_class`
173
174 The traffic class of ICMP packets. Provides a defined level of service to packets by setting the DS Field (formerly TOS) or the Traffic Class field of IP headers. Packets are delivered with the minimum priority by default (Best-effort delivery). Intermediate routers must be able to support this feature.
175
176 *Only available on Unix systems. Ignored on Windows.*
177
178 - Type: `int`
179 - Default: `0`
125180
126181 #### Return value
127 - `Host` object
128
129 A `Host` object containing statistics about the desired destination:<br>
130 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`,<br>
131 `packets_received`, `packet_loss`, `is_alive`.
182
183 - A `Host` object containing statistics about the desired destination:<br>
184 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `rtts`, `packets_sent`, `packets_received`, `packet_loss`, `jitter`, `is_alive`
132185
133186 #### Exceptions
134 - `SocketPermissionError`
135
136 If the permissions are insufficient to create a socket.
187
188 - [`NameLookupError`]
189
190 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
191
192 - [`SocketPermissionError`]
193
194 If the privileges are insufficient to create the socket.
195
196 - [`SocketAddressError`]
197
198 If the source address cannot be assigned to the socket.
199
200 - [`ICMPSocketError`]
201
202 If another error occurs. See the [`ICMPv4Socket`] or [`ICMPv6Socket`] class for details.
137203
138204 #### Example
139 ```python
205
206 ```python
207 >>> from icmplib import ping
208
140209 >>> host = ping('1.1.1.1', count=10, interval=0.2)
141210
142 >>> host.address # The IP address of the gateway or host
143 '1.1.1.1' # that responded to the request
144
145 >>> host.min_rtt # The minimum round-trip time
146 12.2
147
148 >>> host.avg_rtt # The average round-trip time
149 13.2
150
151 >>> host.max_rtt # The maximum round-trip time
152 17.6
153
154 >>> host.packets_sent # The number of packets transmitted to the
155 10 # destination host
156
157 >>> host.packets_received # The number of packets sent by the remote
158 9 # host and received by the current host
159
160 >>> host.packet_loss # Packet loss occurs when packets fail to
161 0.1 # reach their destination. Returns a float
162 # between 0 and 1 (all packets are lost)
163 >>> host.is_alive # Indicates whether the host is reachable
211 >>> host.address # The IP address of the host that responded
212 '1.1.1.1' # to the request
213
214 >>> host.min_rtt # The minimum round-trip time in milliseconds
215 5.761
216
217 >>> host.avg_rtt # The average round-trip time in milliseconds
218 12.036
219
220 >>> host.max_rtt # The maximum round-trip time in milliseconds
221 16.207
222
223 >>> host.rtts # The list of round-trip times expressed in
224 [ 11.595, 13.135, 9.614, # milliseconds
225 16.018, 11.960, 5.761, # The results are not rounded unlike other
226 16.207, 11.937, 12.098 ] # properties
227
228 >>> host.packets_sent # The number of requests transmitted to the
229 10 # remote host
230
231 >>> host.packets_received # The number of ICMP responses received from
232 9 # the remote host
233
234 >>> host.packet_loss # Packet loss occurs when packets fail to
235 0.1 # reach their destination. Returns a float
236 # between 0 and 1 (all packets are lost)
237
238 >>> host.jitter # The jitter in milliseconds, defined as the
239 4.575 # variance of the latency of packets flowing
240 # through the network
241
242 >>> host.is_alive # Indicates whether the host is reachable
164243 True
165244 ```
166245
167246 <br>
168247
169 ### Multiping
170 Send *ICMP ECHO_REQUEST* packets to multiple network hosts.
171
172 #### Definition
173 ```python
174 multiping(addresses, count=2, interval=1, timeout=2, id=PID, max_threads=10, **kwargs)
248 ### multiping
249
250 Send ICMP Echo Request packets to several network hosts.
251
252 ```python
253 multiping(addresses, count=2, interval=0.5, timeout=2, concurrent_tasks=50, source=None, family=None, privileged=True, **kwargs)
175254 ```
176255
177256 #### Parameters
257
178258 - `addresses`
179259
180 The IP addresses of the gateways or hosts to which messages should be sent.
181
182 - Type: `list of str`
260 The IP addresses of the hosts to which messages should be sent. Hostnames and FQDNs are allowed but not recommended. You can easily retrieve their IP address by calling the built-in [`resolve`] function.
261
262 - Type: `list[str]`
183263
184264 - `count`
185265
193273 The interval in seconds between sending each packet.
194274
195275 - Type: `int` or `float`
196 - Default: `1`
276 - Default: `0.5`
197277
198278 - `timeout`
199279
202282 - Type: `int` or `float`
203283 - Default: `2`
204284
205 - `id`
206
207 The identifier of the requests. This identifier will be incremented by one for each destination.
208
209 - Type: `int`
210 - Default: `PID`
211
212 - `max_threads`
213
214 The number of threads allowed to speed up processing.
215
216 - Type: `int`
217 - Default: `10`
218
219 - `**kwargs`
220
221 `Optional` Advanced use: arguments passed to `ICMPRequest` objects.
285 - `concurrent_tasks`
286
287 The maximum number of concurrent tasks to speed up processing. This value cannot exceed the maximum number of file descriptors configured on the operating system.
288
289 - Type: `int`
290 - Default: `50`
291
292 - `source`
293
294 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destinations. This parameter should not be used if you are passing both IPv4 and IPv6 addresses to this function.
295
296 - Type: `str`
297 - Default: `None`
298
299 - `family`
300
301 The address family if a hostname or FQDN is specified. Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default, this function searches for IPv4 addresses first before searching for IPv6 addresses.
302
303 - Type: `int`
304 - Default: `None`
305
306 - `privileged`
307
308 When this option is enabled, this library fully manages the exchanges and the structure of ICMP packets. Disable this option if you want to use this function without root privileges and let the kernel handle ICMP headers.
309
310 [Learn more about the `privileged` parameter.]
311
312 *Only available on Unix systems. Ignored on Windows.*
313
314 - Type: `bool`
315 - Default: `True`
316
317 - `payload`
318
319 The payload content in bytes. A random payload is used by default.
320
321 - Type: `bytes`
322 - Default: `None`
323
324 - `payload_size`
325
326 The payload size. Ignored when the `payload` parameter is set.
327
328 - Type: `int`
329 - Default: `56`
330
331 - `traffic_class`
332
333 The traffic class of ICMP packets. Provides a defined level of service to packets by setting the DS Field (formerly TOS) or the Traffic Class field of IP headers. Packets are delivered with the minimum priority by default (Best-effort delivery). Intermediate routers must be able to support this feature.
334
335 *Only available on Unix systems. Ignored on Windows.*
336
337 - Type: `int`
338 - Default: `0`
222339
223340 #### Return value
224 - `List of Host`
225
226 A `list of Host` objects containing statistics about the desired destinations:<br>
227 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`,<br>
228 `packets_received`, `packet_loss`, `is_alive`.<br>
341
342 - A list of `Host` objects containing statistics about the desired destinations:<br>
343 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `rtts`, `packets_sent`, `packets_received`, `packet_loss`, `jitter`, `is_alive`
344
229345 The list is sorted in the same order as the addresses passed in parameters.
230346
231347 #### Exceptions
232 - `SocketPermissionError`
233
234 If the permissions are insufficient to create a socket.
348
349 - [`NameLookupError`]
350
351 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
352
353 - [`SocketPermissionError`]
354
355 If the privileges are insufficient to create the socket.
356
357 - [`SocketAddressError`]
358
359 If the source address cannot be assigned to the socket.
360
361 - [`ICMPSocketError`]
362
363 If another error occurs. See the [`ICMPv4Socket`] or [`ICMPv6Socket`] class for details.
235364
236365 #### Example
237 ```python
366
367 ```python
368 >>> from icmplib import multiping
369
238370 >>> hosts = multiping(['10.0.0.5', '127.0.0.1', '::1'])
239371
240372 >>> for host in hosts:
241373 ... if host.is_alive:
242374 ... # See the Host class for details
243 ... print(f'{host.address} is alive!')
244 ...
375 ... print(f'{host.address} is up!')
245376 ... else:
246 ... print(f'{host.address} is dead!')
247 ...
248
249 # 10.0.0.5 is dead!
250 # 127.0.0.1 is alive!
251 # ::1 is alive!
252 ```
253
254 <br>
255
256 ### Traceroute
377 ... print(f'{host.address} is down!')
378
379 # 10.0.0.5 is down!
380 # 127.0.0.1 is up!
381 # ::1 is up!
382 ```
383
384 <br>
385
386 ### traceroute
387
257388 Determine the route to a destination host.
258389
259 The Internet is a large and complex aggregation of network hardware, connected together by gateways. Tracking the route one's packets follow can be difficult. This function utilizes the IP protocol time to live field and attempts to elicit an *ICMP TIME_EXCEEDED* response from each gateway along the path to some host.
260
261 #### Definition
262 ```python
263 traceroute(address, count=3, interval=0.05, timeout=2, id=PID, traffic_class=0, max_hops=30, fast_mode=False, **kwargs)
390 The Internet is a large and complex aggregation of network hardware, connected together by gateways. Tracking the route one's packets follow can be difficult. This function uses the IP protocol time to live field and attempts to elicit an ICMP Time Exceeded response from each gateway along the path to some host.
391
392 *This function requires root privileges to run.*
393
394 ```python
395 traceroute(address, count=2, interval=0.05, timeout=2, first_hop=1, max_hops=30, fast=False, id=None, source=None, family=None, **kwargs)
264396 ```
265397
266398 #### Parameters
399
267400 - `address`
268401
269 The destination IP address.
402 The IP address, hostname or FQDN of the host to reach. For deterministic behavior, prefer to use an IP address.
270403
271404 - Type: `str`
272405
275408 The number of ping to perform per hop.
276409
277410 - Type: `int`
278 - Default: `3`
411 - Default: `2`
279412
280413 - `interval`
281414
291424 - Type: `int` or `float`
292425 - Default: `2`
293426
294 - `id`
295
296 The identifier of the request. Used to match the reply with the request.<br>
297 In practice, a unique identifier is used for every ping process.
298
299 - Type: `int`
300 - Default: `PID`
301
302 - `traffic_class`
303
304 The traffic class of packets. Provides a defined level of service to packets by setting the DS Field (formerly TOS) or the Traffic Class field of IP headers. Packets are delivered with the minimum priority by default (Best-effort delivery).
305
306 Intermediate routers must be able to support this feature.<br>
307 *Only available on Unix systems. Ignored on Windows.*
308
309 - Type: `int`
310 - Default: `0`
427 - `first_hop`
428
429 The initial time to live value used in outgoing probe packets.
430
431 - Type: `int`
432 - Default: `1`
311433
312434 - `max_hops`
313435
316438 - Type: `int`
317439 - Default: `30`
318440
319 - `fast_mode`
320
321 When this option is enabled and an intermediate router has been reached, skip to the next hop rather than perform additional requests. The `count` parameter then becomes the maximum number of requests in case of no responses.
441 - `fast`
442
443 When this option is enabled and an intermediate router has been reached, skip to the next hop rather than perform additional requests. The `count` parameter then becomes the maximum number of requests in the event of no response.
322444
323445 - Type: `bool`
324446 - Default: `False`
325447
326 - `**kwargs`
327
328 `Optional` Advanced use: arguments passed to `ICMPRequest` objects.
448 - `id`
449
450 The identifier of ICMP requests. Used to match the responses with requests. In practice, a unique identifier should be used for every traceroute process. The library handles this identifier itself by default.
451
452 - Type: `int`
453 - Default: `None`
454
455 - `source`
456
457 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination.
458
459 - Type: `str`
460 - Default: `None`
461
462 - `family`
463
464 The address family if a hostname or FQDN is specified. Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default, this function searches for IPv4 addresses first before searching for IPv6 addresses.
465
466 - Type: `int`
467 - Default: `None`
468
469 - `payload`
470
471 The payload content in bytes. A random payload is used by default.
472
473 - Type: `bytes`
474 - Default: `None`
475
476 - `payload_size`
477
478 The payload size. Ignored when the `payload` parameter is set.
479
480 - Type: `int`
481 - Default: `56`
482
483 - `traffic_class`
484
485 The traffic class of ICMP packets. Provides a defined level of service to packets by setting the DS Field (formerly TOS) or the Traffic Class field of IP headers. Packets are delivered with the minimum priority by default (Best-effort delivery). Intermediate routers must be able to support this feature.
486
487 *Only available on Unix systems. Ignored on Windows.*
488
489 - Type: `int`
490 - Default: `0`
329491
330492 #### Return value
331 - `List of Hop`
332
333 A `list of Hop` objects representing the route to the desired host. A `Hop` is a `Host` object with an additional attribute: a `distance`. The list is sorted in ascending order according to the distance (in terms of hops) that separates the remote host from the current machine.
493
494 - A list of `Hop` objects representing the route to the desired destination. A `Hop` has the same properties as a `Host` object but it also has a `distance`:<br>
495 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `rtts`, `packets_sent`, `packets_received`, `packet_loss`, `jitter`, `is_alive`, `distance`
496
497 The list is sorted in ascending order according to the distance, in terms of hops, that separates the remote host from the current machine. Gateways that do not respond to requests are not added to this list.
334498
335499 #### Exceptions
336 - `SocketPermissionError`
337
338 If the permissions are insufficient to create a socket.
500
501 - [`NameLookupError`]
502
503 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
504
505 - [`SocketPermissionError`]
506
507 If the privileges are insufficient to create the socket.
508
509 - [`SocketAddressError`]
510
511 If the source address cannot be assigned to the socket.
512
513 - [`ICMPSocketError`]
514
515 If another error occurs. See the [`ICMPv4Socket`] or [`ICMPv6Socket`] class for details.
339516
340517 #### Example
341 ```python
518
519 ```python
520 >>> from icmplib import traceroute
521
342522 >>> hops = traceroute('1.1.1.1')
343523
344 >>> print('Distance (ttl) Address Average round-trip time')
524 >>> print('Distance/TTL Address Average round-trip time')
345525 >>> last_distance = 0
346526
347527 >>> for hop in hops:
348528 ... if last_distance + 1 != hop.distance:
349 ... print('Some routers are not responding')
529 ... print('Some gateways are not responding')
350530 ...
351531 ... # See the Hop class for details
352532 ... print(f'{hop.distance} {hop.address} {hop.avg_rtt} ms')
353533 ...
354534 ... last_distance = hop.distance
535
536 # Distance/TTL Address Average round-trip time
537 # 1 10.0.0.1 5.196 ms
538 # 2 194.149.169.49 7.552 ms
539 # 3 194.149.166.54 12.21 ms
540 # * Some gateways are not responding
541 # 5 212.73.205.22 22.15 ms
542 # 6 1.1.1.1 13.59 ms
543 ```
544
545 <br>
546
547 ### async_ping
548
549 Send ICMP Echo Request packets to a network host.
550
551 *This function is non-blocking.*
552
553 ```python
554 async_ping(address, count=4, interval=1, timeout=2, id=None, source=None, family=None, privileged=True, **kwargs)
555 ```
556
557 #### Parameters, return value and exceptions
558
559 The same parameters, return value and exceptions as for the [`ping`] function.
560
561 #### Example
562
563 ```python
564 >>> import asyncio
565 >>> from icmplib import async_ping
566
567 >>> async def is_alive(address):
568 ... host = await async_ping(address, count=10, interval=0.2)
569 ... return host.is_alive
570
571 >>> asyncio.run(is_alive('1.1.1.1'))
572 True
573 ```
574
575 <br>
576
577 ### async_multiping
578
579 Send ICMP Echo Request packets to several network hosts.
580
581 *This function is non-blocking.*
582
583 ```python
584 async_multiping(addresses, count=2, interval=0.5, timeout=2, concurrent_tasks=50, source=None, family=None, privileged=True, **kwargs)
585 ```
586
587 #### Parameters, return value and exceptions
588
589 The same parameters, return values and exceptions as for the [`multiping`] function.
590
591 #### Example
592
593 ```python
594 >>> import asyncio
595 >>> from icmplib import async_multiping
596
597 >>> async def are_alive(*addresses):
598 ... hosts = await async_multiping(addresses)
599 ...
600 ... for host in hosts:
601 ... if not host.is_alive:
602 ... return False
355603 ...
356
357 # Distance (ttl) Address Average round-trip time
358 # 1 10.0.0.1 5.196 ms
359 # 2 194.149.169.49 7.552 ms
360 # 3 194.149.166.54 12.21 ms
361 # * Some routers are not responding
362 # 5 212.73.205.22 22.15 ms
363 # 6 1.1.1.1 13.59 ms
364 ```
365
366 <br>
367
368 ## ICMP sockets
369
370 If you want to create your own functions and classes using the ICMP protocol, you can use the `ICMPv4Socket` (for IPv4) and the `ICMPv6Socket` (for IPv6 only). These classes have many methods and attributes in common. They manipulate `ICMPRequest` and `ICMPReply` objects.
371
372 ```
373 ┌─────────────────┐
374 ┌─────────────────┐ send(...) │ ICMPSocket: │ receive() ┌─────────────────┐
375 │ ICMPRequest │ ────────────> │ ICMPv4Socket or │ ────────────> │ ICMPReply │
376 └─────────────────┘ │ ICMPv6Socket │ └─────────────────┘
377 └─────────────────┘
378 ```
379
380 ### ICMPRequest
381 A user-created object that represents an *ICMP ECHO_REQUEST*.
382
383 #### Definition
384 ```python
385 ICMPRequest(destination, id, sequence, payload=None, payload_size=56, timeout=2, ttl=64, traffic_class=0)
386 ```
387
388 #### Parameters / Getters
389 - `destination`
390
391 The IP address of the gateway or host to which the message should be sent.
392
393 - Type: `str`
394
395 - `id`
396
397 The identifier of the request. Used to match the reply with the request.<br>
398 In practice, a unique identifier is used for every ping process.
399
400 - Type: `int`
401
402 - `sequence`
403
404 The sequence number. Used to match the reply with the request.<br>
405 Typically, the sequence number is incremented for each packet sent during the process.
406
407 - Type: `int`
408
409 - `payload`
410
411 The payload content in bytes. A random payload is used by default.
412
413 - Type: `bytes`
414 - Default: `None`
415
416 - `payload_size`
417
418 The payload size. Ignored when the `payload` parameter is set.
419
420 - Type: `int`
421 - Default: `56`
422
423 - `timeout`
424
425 The maximum waiting time for receiving a reply in seconds.
426
427 - Type: `int` or `float`
428 - Default: `2`
429
430 - `ttl`
431
432 The time to live of the packet in seconds.
433
434 - Type: `int`
435 - Default: `64`
436
437 - `traffic_class`
438
439 The traffic class of the packet. Provides a defined level of service to the packet by setting the DS Field (formerly TOS) or the Traffic Class field of the IP header. Packets are delivered with the minimum priority by default (Best-effort delivery).
440
441 Intermediate routers must be able to support this feature.<br>
442 *Only available on Unix systems. Ignored on Windows.*
443
444 - Type: `int`
445 - Default: `0`
446
447 #### Getters only
448 - `time`
449
450 The timestamp of the ICMP request. Initialized to zero when creating the request and replaced by `ICMPv4Socket` or `ICMPv6Socket` with the time of sending.
451
452 - Type: `float`
453
454 <br>
455
456 ### ICMPReply
457 A class that represents an ICMP reply. Generated from an `ICMPSocket` object (`ICMPv4Socket` or `ICMPv6Socket`).
458
459 #### Definition
460 ```python
461 ICMPReply(source, id, sequence, type, code, bytes_received, time)
462 ```
463
464 #### Parameters / Getters
465 - `source`
466
467 The IP address of the gateway or host that composes the ICMP message.
468
469 - Type: `str`
470
471 - `id`
472
473 The identifier of the request. Used to match the reply with the request.
474
475 - Type: `int`
476
477 - `sequence`
478
479 The sequence number. Used to match the reply with the request.
480
481 - Type: `int`
482
483 - `type`
484
485 The type of message.
486
487 - Type: `int`
488
489 - `code`
490
491 The error code.
492
493 - Type: `int`
494
495 - `bytes_received`
496
497 The number of bytes received.
498
499 - Type: `int`
500
501 - `time`
502
503 The timestamp of the ICMP reply.
504
505 - Type: `float`
506
507 #### Methods
508 - `raise_for_status()`
509
510 Throw an exception if the reply is not an *ICMP ECHO_REPLY*.<br>
511 Otherwise, do nothing.
512
513 - Raises `ICMPv4DestinationUnreachable`: If the ICMPv4 reply is type 3.
514 - Raises `ICMPv4TimeExceeded`: If the ICMPv4 reply is type 11.
515 - Raises `ICMPv6DestinationUnreachable`: If the ICMPv6 reply is type 1.
516 - Raises `ICMPv6TimeExceeded`: If the ICMPv6 reply is type 3.
517 - Raises `ICMPError`: If the reply is of another type and is not an *ICMP ECHO_REPLY*.
518
519 <br>
520
521 ### ICMPv4Socket
522 Socket for sending and receiving ICMPv4 packets.
523
524 #### Definition
525 ```python
526 ICMPv4Socket()
527 ```
528
529 #### Methods
530 - `__init__()`
531
532 *Constructor. Automatically called: do not call it directly.*
533
534 - Raises `SocketPermissionError`: If the permissions are insufficient to create the socket.
535
536 - `__del__()`
537
538 *Destructor. Automatically called: do not call it directly.*
539
540 - Call the `close` method.
541
542 - `send(request)`
543
544 Send a request to a host.
545
546 This operation is non-blocking. Use the `receive` method to get the reply.
547
548 - Parameter `ICMPRequest`: The ICMP request you have created.
549 - Raises `SocketBroadcastError`: If a broadcast address is used and the corresponding option is not enabled on the socket (ICMPv4 only).
550 - Raises `SocketUnavailableError`: If the socket is closed.
551 - Raises `ICMPSocketError`: If another error occurs while sending.
552
553 - `receive()`
554
555 Receive a reply from a host.
556
557 This method can be called multiple times if you expect several responses (as with a broadcast address).
558
559 - Raises `TimeoutExceeded`: If no response is received before the timeout defined in the request. This exception is also useful for stopping a possible loop in case of multiple responses.
560 - Raises `SocketUnavailableError`: If the socket is closed.
561 - Raises `ICMPSocketError`: If another error occurs while receiving.
562 - Returns `ICMPReply`: An `ICMPReply` object containing the reply of the desired destination. See the `ICMPReply` class for details.
563
564 - `close()`
565
566 Close the socket. It cannot be used after this call.
567
568 #### Getters only
569 - `is_closed`
570
571 Indicate whether the socket is closed.
572
573 - Type: `bool`
574
575 #### Getters / Setters
576 - `broadcast`
577
578 Enable or disable the broadcast support on the socket.
579
580 - Type: `bool`
581 - Default: `False`
582
583 <br>
584
585 ### ICMPv6Socket
586 Socket for sending and receiving ICMPv6 packets.
587
588 #### Definition
589 ```python
590 ICMPv6Socket()
591 ```
592
593 #### Methods
594 The same methods as for the `ICMPv4Socket` class.
595
596 <br>
597
598 ### Exceptions
599 The library contains many exceptions to adapt to your needs:
600
601 ```
602 ICMPLibError
603 ├─ ICMPSocketError
604 │ ├─ SocketPermissionError
605 │ ├─ SocketUnavailableError
606 │ ├─ SocketBroadcastError
607 │ └─ TimeoutExceeded
608
609 └─ ICMPError
610 ├─ DestinationUnreachable
611 │ ├─ ICMPv4DestinationUnreachable
612 │ └─ ICMPv6DestinationUnreachable
613
614 └─ TimeExceeded
615 ├─ ICMPv4TimeExceeded
616 └─ ICMPv6TimeExceeded
617 ```
618
619 - `ICMPLibError`: Exception class for the icmplib package.
620 - `ICMPSocketError`: Base class for ICMP sockets exceptions.
621 - `SocketPermissionError`: Raised when the permissions are insufficient to create a socket.
622 - `SocketUnavailableError`: Raised when an action is performed while the socket is closed.
623 - `SocketBroadcastError`: Raised when a broadcast address is used and the corresponding option is not enabled on the socket.
624 - `TimeoutExceeded`: Raised when a timeout occurs on a socket.
625 - `ICMPError`: Base class for ICMP error messages.
626 - `DestinationUnreachable`: Destination Unreachable message is generated by the host or its inbound gateway to inform the client that the destination is unreachable for some reason.
627 - `TimeExceeded`: Time Exceeded message is generated by a gateway to inform the source of a discarded datagram due to the time to live field reaching zero. A Time Exceeded message may also be sent by a host if it fails to reassemble a fragmented datagram within its time limit.
628
629 Use the `message` property to retrieve the error message.
630
631 `ICMPError` subclasses have properties to retrieve the response (`reply` property) and the specific message of the error (`message` property).
632
633 <br>
634
635 ### Examples
636 #### Sockets in action
637 ```python
638 def single_ping(address, timeout=2, id=PID):
639 # Create an ICMP socket
640 socket = ICMPv4Socket()
641
642 # Create a request
643 # See the ICMPRequest class for details
644 request = ICMPRequest(
645 destination=address,
646 id=id,
647 sequence=1,
648 timeout=timeout)
649
650 try:
651 socket.send(request)
652
653 # If the program arrives in this section,
654 # it means that the packet has been transmitted
655
656 reply = socket.receive()
657
658 # If the program arrives in this section,
659 # it means that a packet has been received
660 # The reply has the same identifier and sequence number that
661 # the request but it can come from an intermediate gateway
662
663 reply.raise_for_status()
664
665 # If the program arrives in this section,
666 # it means that the destination host has responded to
667 # the request
668
669 except TimeoutExceeded as err:
670 # The timeout has been reached
671 # Equivalent to print(err.message)
672 print(err)
673
674 except DestinationUnreachable as err:
675 # The reply indicates that the destination host is
676 # unreachable
677 print(err)
678
679 # Retrieve the response
680 reply = err.reply
681
682 except TimeExceeded as err:
683 # The reply indicates that the time to live exceeded
684 # in transit
685 print(err)
686
687 # Retrieve the response
688 reply = err.reply
689
690 except ICMPLibError as err:
691 # All other errors
692 print(err)
693
694 # Automatic socket closure (garbage collector)
695 ```
696
697 #### Verbose ping
698 ```python
699 def verbose_ping(address, count=4, interval=1, timeout=2, id=PID):
700 # ICMPRequest uses a payload of 56 bytes by default
701 # You can modify it using the payload_size parameter
702 print(f'PING {address}: 56 data bytes')
703
704 # Detection of the socket to use
705 if is_ipv6_address(address):
706 socket = ICMPv6Socket()
707
708 else:
709 socket = ICMPv4Socket()
710
711 for sequence in range(count):
712 # We create an ICMP request
713 request = ICMPRequest(
714 destination=address,
715 id=id,
716 sequence=sequence,
717 timeout=timeout)
718
719 try:
720 # We send the request
721 socket.send(request)
722
723 # We are awaiting receipt of an ICMP reply
724 reply = socket.receive()
725
726 # We received a reply
727 # We display some information
728 print(f'{reply.bytes_received} bytes from '
729 f'{reply.source}: ', end='')
730
731 # We throw an exception if it is an ICMP error message
732 reply.raise_for_status()
733
734 # We calculate the round-trip time and we display it
735 round_trip_time = (reply.time - request.time) * 1000
736
737 print(f'icmp_seq={sequence} '
738 f'time={round(round_trip_time, 3)} ms')
739
740 # We pause before continuing
741 if sequence < count - 1:
742 sleep(interval)
743
744 except TimeoutExceeded:
745 # The timeout has been reached
746 print(f'Request timeout for icmp_seq {sequence}')
747
748 except ICMPError as err:
749 # An ICMP error message has been received
750 print(err)
751
752 except ICMPLibError:
753 # All other errors
754 print('An error has occurred.')
755
756
757 verbose_ping('1.1.1.1')
758
759 # PING 1.1.1.1: 56 data bytes
760 # 64 bytes from 1.1.1.1: icmp_seq=0 time=12.061 ms
761 # 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms
762 # 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms
763 # 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms
764 ```
765
766 <br>
767
768 ## FAQ
769
770 ### How to resolve a FQDN / domain name?
771 Python has a method to do this in its libraries:
772 ```python
773 >>> import socket
774 >>> socket.gethostbyname('github.com')
775 '140.82.118.4'
776 ```
604 ... return True
605
606 >>> asyncio.run(are_alive('10.0.0.5', '127.0.0.1', '::1'))
607 False
608 ```
609
610 <br>
611
612 ## Documentation
613
614 This page only gives an overview of the features of icmplib.
615
616 To learn more about the built-in functions, on how to create your own and handle exceptions, you can click on the following link:
617
618 - :rocket: [Documentation]
777619
778620 ## Contributing
779621
780622 Comments and enhancements are welcome.
781623
782 All development is done on [GitHub](https://github.com/ValentinBELYN/icmplib). Use [Issues](https://github.com/ValentinBELYN/icmplib/issues) to report problems and submit feature requests. Please include a minimal example that reproduces the bug.
624 All development is done on [GitHub]. Use [Issues] to report problems and submit feature requests. Please include a minimal example that reproduces the bug.
783625
784626 ## Donate
785627
786628 icmplib is completely free and open source. It has been fully developed on my free time. If you enjoy it, please consider donating to support the development.
787629
788 - [:tada: Donate via PayPal](https://paypal.me/ValentinBELYN)
630 - :tada: [Donate via PayPal]
789631
790632 ## License
791633
792 Copyright 2017-2020 Valentin BELYN.
793
794 Code released under the GNU LGPLv3 license. See the [LICENSE](LICENSE) for details.
634 Copyright 2017-2021 Valentin BELYN.
635
636 Code released under the GNU LGPLv3 license. See the [LICENSE] for details.
637
638 [Learn more about the `privileged` parameter.]: docs/6-use-icmplib-without-privileges.md
639 [Documentation]: docs
640 [GitHub]: https://github.com/ValentinBELYN/icmplib
641 [Issues]: https://github.com/ValentinBELYN/icmplib/issues
642 [Donate via PayPal]: https://paypal.me/ValentinBELYN
643 [LICENSE]: LICENSE
644 [`ping`]: #ping
645 [`multiping`]: #multiping
646 [`ICMPv4Socket`]: docs/3-sockets.md#ICMPv4Socket
647 [`ICMPv6Socket`]: docs/3-sockets.md#ICMPv6Socket
648 [`NameLookupError`]: docs/4-exceptions.md#NameLookupError
649 [`ICMPSocketError`]: docs/4-exceptions.md#ICMPSocketError
650 [`SocketAddressError`]: docs/4-exceptions.md#SocketAddressError
651 [`SocketPermissionError`]: docs/4-exceptions.md#SocketPermissionError
652 [`resolve`]: docs/5-utilities.md#resolve
0 # Installation
1
2 ## Requirements
3
4 - Since icmplib 3, Python 3.7 or later is required to use the library.
5 - If you are using Python 3.6 and you cannot update it, you can still install icmplib 2.
6 - icmplib has been optimized to run on the most popular operating systems such as Linux, macOS and Windows.
7
8 ## Installation and update
9
10 - **Install**
11
12 The recommended way to install icmplib is to use `pip3`:
13
14 ```shell
15 $ pip3 install icmplib
16 ```
17
18 - **Update**
19
20 Before updating icmplib, take care to read the release notes carefully to avoid any incompatibilities with your applications.
21
22 ```shell
23 $ pip3 install --upgrade icmplib
24 ```
25
26 ## Imports
27
28 Add the following instructions into your project to import icmplib (only import what you need):
29
30 - **Basic functions**
31
32 ```python
33 from icmplib import ping, multiping, traceroute, resolve
34 ```
35
36 - **Asynchronous functions**
37
38 ```python
39 from icmplib import async_ping, async_multiping, async_resolve
40 ```
41
42 - **Sockets (advanced)**
43
44 ```python
45 from icmplib import ICMPv4Socket, ICMPv6Socket, AsyncSocket, ICMPRequest, ICMPReply
46 ```
47
48 - **Exceptions**
49
50 ```python
51 from icmplib import ICMPLibError, NameLookupError, ICMPSocketError
52 from icmplib import SocketAddressError, SocketPermissionError
53 from icmplib import SocketUnavailableError, SocketBroadcastError, TimeoutExceeded
54 from icmplib import ICMPError, DestinationUnreachable, TimeExceeded
55 ```
0 # Built-in functions
1
2 ### ping
3
4 Send ICMP Echo Request packets to a network host.
5
6 ```python
7 ping(address, count=4, interval=1, timeout=2, id=None, source=None, family=None, privileged=True, **kwargs)
8 ```
9
10 #### Parameters
11
12 - `address`
13
14 The IP address, hostname or FQDN of the host to which messages should be sent. For deterministic behavior, prefer to use an IP address.
15
16 - Type: `str`
17
18 - `count`
19
20 The number of ping to perform.
21
22 - Type: `int`
23 - Default: `4`
24
25 - `interval`
26
27 The interval in seconds between sending each packet.
28
29 - Type: `int` or `float`
30 - Default: `1`
31
32 - `timeout`
33
34 The maximum waiting time for receiving a reply in seconds.
35
36 - Type: `int` or `float`
37 - Default: `2`
38
39 - `id`
40
41 The identifier of ICMP requests. Used to match the responses with requests. In practice, a unique identifier should be used for every ping process. On Linux, this identifier is ignored when the `privileged` parameter is disabled. The library handles this identifier itself by default.
42
43 - Type: `int`
44 - Default: `None`
45
46 - `source`
47
48 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination.
49
50 - Type: `str`
51 - Default: `None`
52
53 - `family`
54
55 The address family if a hostname or FQDN is specified. Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default, this function searches for IPv4 addresses first before searching for IPv6 addresses.
56
57 - Type: `int`
58 - Default: `None`
59
60 - `privileged`
61
62 When this option is enabled, this library fully manages the exchanges and the structure of ICMP packets. Disable this option if you want to use this function without root privileges and let the kernel handle ICMP headers.
63
64 [Learn more about the `privileged` parameter.]
65
66 *Only available on Unix systems. Ignored on Windows.*
67
68 - Type: `bool`
69 - Default: `True`
70
71 - `payload`
72
73 The payload content in bytes. A random payload is used by default.
74
75 - Type: `bytes`
76 - Default: `None`
77
78 - `payload_size`
79
80 The payload size. Ignored when the `payload` parameter is set.
81
82 - Type: `int`
83 - Default: `56`
84
85 - `traffic_class`
86
87 The traffic class of ICMP packets. Provides a defined level of service to packets by setting the DS Field (formerly TOS) or the Traffic Class field of IP headers. Packets are delivered with the minimum priority by default (Best-effort delivery). Intermediate routers must be able to support this feature.
88
89 *Only available on Unix systems. Ignored on Windows.*
90
91 - Type: `int`
92 - Default: `0`
93
94 #### Return value
95
96 - A `Host` object containing statistics about the desired destination:<br>
97 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `rtts`, `packets_sent`, `packets_received`, `packet_loss`, `jitter`, `is_alive`
98
99 #### Exceptions
100
101 - [`NameLookupError`]
102
103 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
104
105 - [`SocketPermissionError`]
106
107 If the privileges are insufficient to create the socket.
108
109 - [`SocketAddressError`]
110
111 If the source address cannot be assigned to the socket.
112
113 - [`ICMPSocketError`]
114
115 If another error occurs. See the [`ICMPv4Socket`] or [`ICMPv6Socket`] class for details.
116
117 #### Example
118
119 ```python
120 >>> from icmplib import ping
121
122 >>> host = ping('1.1.1.1', count=10, interval=0.2)
123
124 >>> host.address # The IP address of the host that responded
125 '1.1.1.1' # to the request
126
127 >>> host.min_rtt # The minimum round-trip time in milliseconds
128 5.761
129
130 >>> host.avg_rtt # The average round-trip time in milliseconds
131 12.036
132
133 >>> host.max_rtt # The maximum round-trip time in milliseconds
134 16.207
135
136 >>> host.rtts # The list of round-trip times expressed in
137 [ 11.595, 13.135, 9.614, # milliseconds
138 16.018, 11.960, 5.761, # The results are not rounded unlike other
139 16.207, 11.937, 12.098 ] # properties
140
141 >>> host.packets_sent # The number of requests transmitted to the
142 10 # remote host
143
144 >>> host.packets_received # The number of ICMP responses received from
145 9 # the remote host
146
147 >>> host.packet_loss # Packet loss occurs when packets fail to
148 0.1 # reach their destination. Returns a float
149 # between 0 and 1 (all packets are lost)
150
151 >>> host.jitter # The jitter in milliseconds, defined as the
152 4.575 # variance of the latency of packets flowing
153 # through the network
154
155 >>> host.is_alive # Indicates whether the host is reachable
156 True
157 ```
158
159 <br>
160
161 ### multiping
162
163 Send ICMP Echo Request packets to several network hosts.
164
165 ```python
166 multiping(addresses, count=2, interval=0.5, timeout=2, concurrent_tasks=50, source=None, family=None, privileged=True, **kwargs)
167 ```
168
169 #### Parameters
170
171 - `addresses`
172
173 The IP addresses of the hosts to which messages should be sent. Hostnames and FQDNs are allowed but not recommended. You can easily retrieve their IP address by calling the built-in [`resolve`] function.
174
175 - Type: `list[str]`
176
177 - `count`
178
179 The number of ping to perform per address.
180
181 - Type: `int`
182 - Default: `2`
183
184 - `interval`
185
186 The interval in seconds between sending each packet.
187
188 - Type: `int` or `float`
189 - Default: `0.5`
190
191 - `timeout`
192
193 The maximum waiting time for receiving a reply in seconds.
194
195 - Type: `int` or `float`
196 - Default: `2`
197
198 - `concurrent_tasks`
199
200 The maximum number of concurrent tasks to speed up processing. This value cannot exceed the maximum number of file descriptors configured on the operating system.
201
202 - Type: `int`
203 - Default: `50`
204
205 - `source`
206
207 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destinations. This parameter should not be used if you are passing both IPv4 and IPv6 addresses to this function.
208
209 - Type: `str`
210 - Default: `None`
211
212 - `family`
213
214 The address family if a hostname or FQDN is specified. Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default, this function searches for IPv4 addresses first before searching for IPv6 addresses.
215
216 - Type: `int`
217 - Default: `None`
218
219 - `privileged`
220
221 When this option is enabled, this library fully manages the exchanges and the structure of ICMP packets. Disable this option if you want to use this function without root privileges and let the kernel handle ICMP headers.
222
223 [Learn more about the `privileged` parameter.]
224
225 *Only available on Unix systems. Ignored on Windows.*
226
227 - Type: `bool`
228 - Default: `True`
229
230 - `payload`
231
232 The payload content in bytes. A random payload is used by default.
233
234 - Type: `bytes`
235 - Default: `None`
236
237 - `payload_size`
238
239 The payload size. Ignored when the `payload` parameter is set.
240
241 - Type: `int`
242 - Default: `56`
243
244 - `traffic_class`
245
246 The traffic class of ICMP packets. Provides a defined level of service to packets by setting the DS Field (formerly TOS) or the Traffic Class field of IP headers. Packets are delivered with the minimum priority by default (Best-effort delivery). Intermediate routers must be able to support this feature.
247
248 *Only available on Unix systems. Ignored on Windows.*
249
250 - Type: `int`
251 - Default: `0`
252
253 #### Return value
254
255 - A list of `Host` objects containing statistics about the desired destinations:<br>
256 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `rtts`, `packets_sent`, `packets_received`, `packet_loss`, `jitter`, `is_alive`
257
258 The list is sorted in the same order as the addresses passed in parameters.
259
260 #### Exceptions
261
262 - [`NameLookupError`]
263
264 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
265
266 - [`SocketPermissionError`]
267
268 If the privileges are insufficient to create the socket.
269
270 - [`SocketAddressError`]
271
272 If the source address cannot be assigned to the socket.
273
274 - [`ICMPSocketError`]
275
276 If another error occurs. See the [`ICMPv4Socket`] or [`ICMPv6Socket`] class for details.
277
278 #### Example
279
280 ```python
281 >>> from icmplib import multiping
282
283 >>> hosts = multiping(['10.0.0.5', '127.0.0.1', '::1'])
284
285 >>> for host in hosts:
286 ... if host.is_alive:
287 ... # See the Host class for details
288 ... print(f'{host.address} is up!')
289 ... else:
290 ... print(f'{host.address} is down!')
291
292 # 10.0.0.5 is down!
293 # 127.0.0.1 is up!
294 # ::1 is up!
295 ```
296
297 <br>
298
299 ### traceroute
300
301 Determine the route to a destination host.
302
303 The Internet is a large and complex aggregation of network hardware, connected together by gateways. Tracking the route one's packets follow can be difficult. This function uses the IP protocol time to live field and attempts to elicit an ICMP Time Exceeded response from each gateway along the path to some host.
304
305 *This function requires root privileges to run.*
306
307 ```python
308 traceroute(address, count=2, interval=0.05, timeout=2, first_hop=1, max_hops=30, fast=False, id=None, source=None, family=None, **kwargs)
309 ```
310
311 #### Parameters
312
313 - `address`
314
315 The IP address, hostname or FQDN of the host to reach. For deterministic behavior, prefer to use an IP address.
316
317 - Type: `str`
318
319 - `count`
320
321 The number of ping to perform per hop.
322
323 - Type: `int`
324 - Default: `2`
325
326 - `interval`
327
328 The interval in seconds between sending each packet.
329
330 - Type: `int` or `float`
331 - Default: `0.05`
332
333 - `timeout`
334
335 The maximum waiting time for receiving a reply in seconds.
336
337 - Type: `int` or `float`
338 - Default: `2`
339
340 - `first_hop`
341
342 The initial time to live value used in outgoing probe packets.
343
344 - Type: `int`
345 - Default: `1`
346
347 - `max_hops`
348
349 The maximum time to live (max number of hops) used in outgoing probe packets.
350
351 - Type: `int`
352 - Default: `30`
353
354 - `fast`
355
356 When this option is enabled and an intermediate router has been reached, skip to the next hop rather than perform additional requests. The `count` parameter then becomes the maximum number of requests in the event of no response.
357
358 - Type: `bool`
359 - Default: `False`
360
361 - `id`
362
363 The identifier of ICMP requests. Used to match the responses with requests. In practice, a unique identifier should be used for every traceroute process. The library handles this identifier itself by default.
364
365 - Type: `int`
366 - Default: `None`
367
368 - `source`
369
370 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination.
371
372 - Type: `str`
373 - Default: `None`
374
375 - `family`
376
377 The address family if a hostname or FQDN is specified. Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default, this function searches for IPv4 addresses first before searching for IPv6 addresses.
378
379 - Type: `int`
380 - Default: `None`
381
382 - `payload`
383
384 The payload content in bytes. A random payload is used by default.
385
386 - Type: `bytes`
387 - Default: `None`
388
389 - `payload_size`
390
391 The payload size. Ignored when the `payload` parameter is set.
392
393 - Type: `int`
394 - Default: `56`
395
396 - `traffic_class`
397
398 The traffic class of ICMP packets. Provides a defined level of service to packets by setting the DS Field (formerly TOS) or the Traffic Class field of IP headers. Packets are delivered with the minimum priority by default (Best-effort delivery). Intermediate routers must be able to support this feature.
399
400 *Only available on Unix systems. Ignored on Windows.*
401
402 - Type: `int`
403 - Default: `0`
404
405 #### Return value
406
407 - A list of `Hop` objects representing the route to the desired destination. A `Hop` has the same properties as a `Host` object but it also has a `distance`:<br>
408 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `rtts`, `packets_sent`, `packets_received`, `packet_loss`, `jitter`, `is_alive`, `distance`
409
410 The list is sorted in ascending order according to the distance, in terms of hops, that separates the remote host from the current machine. Gateways that do not respond to requests are not added to this list.
411
412 #### Exceptions
413
414 - [`NameLookupError`]
415
416 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
417
418 - [`SocketPermissionError`]
419
420 If the privileges are insufficient to create the socket.
421
422 - [`SocketAddressError`]
423
424 If the source address cannot be assigned to the socket.
425
426 - [`ICMPSocketError`]
427
428 If another error occurs. See the [`ICMPv4Socket`] or [`ICMPv6Socket`] class for details.
429
430 #### Example
431
432 ```python
433 >>> from icmplib import traceroute
434
435 >>> hops = traceroute('1.1.1.1')
436
437 >>> print('Distance/TTL Address Average round-trip time')
438 >>> last_distance = 0
439
440 >>> for hop in hops:
441 ... if last_distance + 1 != hop.distance:
442 ... print('Some gateways are not responding')
443 ...
444 ... # See the Hop class for details
445 ... print(f'{hop.distance} {hop.address} {hop.avg_rtt} ms')
446 ...
447 ... last_distance = hop.distance
448
449 # Distance/TTL Address Average round-trip time
450 # 1 10.0.0.1 5.196 ms
451 # 2 194.149.169.49 7.552 ms
452 # 3 194.149.166.54 12.21 ms
453 # * Some gateways are not responding
454 # 5 212.73.205.22 22.15 ms
455 # 6 1.1.1.1 13.59 ms
456 ```
457
458 <br>
459
460 ### async_ping
461
462 Send ICMP Echo Request packets to a network host.
463
464 *This function is non-blocking.*
465
466 ```python
467 async_ping(address, count=4, interval=1, timeout=2, id=None, source=None, family=None, privileged=True, **kwargs)
468 ```
469
470 #### Parameters, return value and exceptions
471
472 The same parameters, return value and exceptions as for the [`ping`] function.
473
474 #### Example
475
476 ```python
477 >>> import asyncio
478 >>> from icmplib import async_ping
479
480 >>> async def is_alive(address):
481 ... host = await async_ping(address, count=10, interval=0.2)
482 ... return host.is_alive
483
484 >>> asyncio.run(is_alive('1.1.1.1'))
485 True
486 ```
487
488 <br>
489
490 ### async_multiping
491
492 Send ICMP Echo Request packets to several network hosts.
493
494 *This function is non-blocking.*
495
496 ```python
497 async_multiping(addresses, count=2, interval=0.5, timeout=2, concurrent_tasks=50, source=None, family=None, privileged=True, **kwargs)
498 ```
499
500 #### Parameters, return value and exceptions
501
502 The same parameters, return values and exceptions as for the [`multiping`] function.
503
504 #### Example
505
506 ```python
507 >>> import asyncio
508 >>> from icmplib import async_multiping
509
510 >>> async def are_alive(*addresses):
511 ... hosts = await async_multiping(addresses)
512 ...
513 ... for host in hosts:
514 ... if not host.is_alive:
515 ... return False
516 ...
517 ... return True
518
519 >>> asyncio.run(are_alive('10.0.0.5', '127.0.0.1', '::1'))
520 False
521 ```
522
523 [`ping`]: #ping
524 [`multiping`]: #multiping
525 [`ICMPv4Socket`]: 3-sockets.md#ICMPv4Socket
526 [`ICMPv6Socket`]: 3-sockets.md#ICMPv6Socket
527 [`NameLookupError`]: 4-exceptions.md#NameLookupError
528 [`ICMPSocketError`]: 4-exceptions.md#ICMPSocketError
529 [`SocketAddressError`]: 4-exceptions.md#SocketAddressError
530 [`SocketPermissionError`]: 4-exceptions.md#SocketPermissionError
531 [`resolve`]: 5-utilities.md#resolve
532 [Learn more about the `privileged` parameter.]: 6-use-icmplib-without-privileges.md
0 # Sockets and classes
1
2 If you want to create your own functions and classes using the ICMP protocol, you can use the [`ICMPv4Socket`] (for IPv4 only) and the [`ICMPv6Socket`] (for IPv6 only). These classes have many methods and properties in common. They manipulate [`ICMPRequest`] and [`ICMPReply`] objects.
3
4 ```
5 ┌──────────────────┐
6 ┌─────────────────┐ send(...) │ ICMPv4Socket │ receive() ┌─────────────────┐
7 │ ICMPRequest │ ────────────> │ or │ ────────────> │ ICMPReply │
8 └─────────────────┘ │ ICMPv6Socket │ └─────────────────┘
9 └──────────────────┘
10 ```
11
12 ### ICMPRequest
13
14 A user-created object that represents an ICMP Echo Request.
15
16 ```python
17 ICMPRequest(destination, id, sequence, payload=None, payload_size=56, ttl=64, traffic_class=0)
18 ```
19
20 #### Parameters and properties
21
22 - `destination`
23
24 The IP address of the host to which the message should be sent.
25
26 - Type: `str`
27
28 - `id`
29
30 The identifier of the request. Used to match the reply with the request. In practice, a unique identifier is used for every ping process. On Linux, this identifier is automatically replaced if the request is sent from an unprivileged socket.
31
32 - Type: `int`
33
34 - `sequence`
35
36 The sequence number. Used to match the reply with the request. Typically, the sequence number is incremented for each packet sent during the process.
37
38 - Type: `int`
39
40 - `payload`
41
42 The payload content in bytes. A random payload is used by default.
43
44 - Type: `bytes`
45 - Default: `None`
46
47 - `payload_size`
48
49 The payload size. Ignored when the `payload` parameter is set.
50
51 - Type: `int`
52 - Default: `56`
53
54 - `ttl`
55
56 The time to live of the packet in terms of hops.
57
58 - Type: `int`
59 - Default: `64`
60
61 - `traffic_class`
62
63 The traffic class of the ICMP packet. Provides a defined level of service to the packet by setting the DS Field (formerly TOS) or the Traffic Class field of the IP header. Packets are delivered with the minimum priority by default (Best-effort delivery). Intermediate routers must be able to support this feature.
64
65 *Only available on Unix systems. Ignored on Windows.*
66
67 - Type: `int`
68 - Default: `0`
69
70 #### Properties only
71
72 - `time`
73
74 The timestamp of the ICMP request. Initialized to zero when creating the request and replaced by the `send` method of an ICMP socket with the time of sending.
75
76 - Type: `float`
77
78 <br>
79
80 ### ICMPReply
81
82 A class that represents an ICMP reply. Generated from an ICMP socket.
83
84 ```python
85 ICMPReply(source, family, id, sequence, type, code, bytes_received, time)
86 ```
87
88 #### Parameters and properties
89
90 - `source`
91
92 The IP address of the host that composes the ICMP message.
93
94 - Type: `str`
95
96 - `family`
97
98 The address family. Can be set to `4` for IPv4 or `6` for IPv6 addresses.
99
100 - Type: `int`
101
102 - `id`
103
104 The identifier of the reply. Used to match the reply with the request.
105
106 - Type: `int`
107
108 - `sequence`
109
110 The sequence number. Used to match the reply with the request.
111
112 - Type: `int`
113
114 - `type`
115
116 The type of ICMP message.
117
118 - Type: `int`
119
120 - `code`
121
122 The ICMP error code.
123
124 - Type: `int`
125
126 - `bytes_received`
127
128 The number of bytes received.
129
130 - Type: `int`
131
132 - `time`
133
134 The timestamp of the ICMP reply.
135
136 - Type: `float`
137
138 #### Methods
139
140 - `raise_for_status()`
141
142 Throw an exception if the reply is not an ICMP Echo Reply. Otherwise, do nothing.
143
144 - Raises [`DestinationUnreachable`]: If the destination is unreachable for some reason.
145 - Raises [`TimeExceeded`]: If the time to live field of the ICMP request has reached zero.
146 - Raises [`ICMPError`]: Raised for any other type and ICMP error code, except ICMP Echo Reply messages.
147
148 <br>
149
150 ### ICMPv4Socket
151
152 Class for sending and receiving ICMPv4 packets.
153
154 ```python
155 ICMPv4Socket(address=None, privileged=True)
156 ```
157
158 #### Parameters
159
160 - `source`
161
162 The IP address from which you want to listen and send packets. By default, the socket listens on all interfaces.
163
164 - Type: `str`
165 - Default: `None`
166
167 - `privileged`
168
169 When this option is enabled, the socket fully manages the exchanges and the structure of the ICMP packets. Disable this option if you want to instantiate and use the socket without root privileges and let the kernel handle ICMP headers.
170
171 *Only available on Unix systems. Ignored on Windows.*
172
173 - Type: `bool`
174 - Default: `True`
175
176 #### Methods
177
178 - `__init__(address=None, privileged=True)`
179
180 *Constructor. Automatically called: do not call it directly.*
181
182 - Raises [`SocketPermissionError`]: If the privileges are insufficient to create the socket.
183 - Raises [`SocketAddressError`]: If the requested address cannot be assigned to the socket.
184 - Raises [`ICMPSocketError`]: If another error occurs while creating the socket.
185
186 - `__enter__()`
187
188 Return this object.
189
190 - `__exit__(type, value, traceback)`
191
192 Call the `close` method.
193
194 - `__del__()`
195
196 *Destructor. Automatically called: do not call it directly.*
197
198 Call the `close` method.
199
200 - `send(request)`
201
202 Send an ICMP request message over the network to a remote host.
203
204 This operation is non-blocking. Use the `receive` method to get the reply.
205
206 - Parameter `request` ([`ICMPRequest`]): The ICMP request you have created. If the socket is used in non-privileged mode on a Linux system, the identifier defined in the request will be replaced by the kernel.
207 - Raises [`SocketBroadcastError`]: If a broadcast address is used and the corresponding option is not enabled on the socket (ICMPv4 only).
208 - Raises [`SocketUnavailableError`]: If the socket is closed.
209 - Raises [`ICMPSocketError`]: If another error occurs while sending.
210
211 - `receive(request=None, timeout=2)`
212
213 Receive an ICMP reply message from the socket.
214
215 This method can be called multiple times if you expect several responses as with a broadcast address.
216
217 - Parameter `request` ([`ICMPRequest`]): The ICMP request to use to match the response. By default, all ICMP packets arriving on the socket are returned.
218 - Parameter `timeout` (`int` or `float`): The maximum waiting time for receiving the response in seconds. Default to `2`.
219 - Raises [`TimeoutExceeded`]: If no response is received before the timeout specified in parameters.
220 - Raises [`SocketUnavailableError`]: If the socket is closed.
221 - Raises [`ICMPSocketError`]: If another error occurs while receiving.
222
223 Returns an [`ICMPReply`] object representing the response of the desired destination or an upstream gateway.
224
225 - `close()`
226
227 Close the socket. It cannot be used after this call.
228
229 #### Properties only
230
231 - `address`
232
233 The IP address from which the socket listens and sends packets. Return `None` if the socket listens on all interfaces.
234
235 - Type: `str`
236
237 - `is_privileged`
238
239 Indicate whether the socket is running in privileged mode.
240
241 - Type: `bool`
242
243 - `is_closed`
244
245 Indicate whether the socket is closed.
246
247 - Type: `bool`
248
249 #### Properties and setters
250
251 - `blocking`
252
253 Set the blocking or non-blocking mode of the socket.
254
255 - Type: `bool`
256 - Default: `False`
257
258 - `broadcast`
259
260 Enable or disable the broadcast support on the socket.
261
262 - Type: `bool`
263 - Default: `False`
264
265 <br>
266
267 ### ICMPv6Socket
268
269 Class for sending and receiving ICMPv6 packets.
270
271 ```python
272 ICMPv6Socket(address=None, privileged=True)
273 ```
274
275 #### Methods and properties
276
277 The same methods and properties as for the [`ICMPv4Socket`] class, except the `broadcast` property.
278
279 <br>
280
281 ### AsyncSocket
282
283 A wrapper for ICMP sockets which makes them asynchronous.
284
285 ```python
286 AsyncSocket(icmp_sock)
287 ```
288
289 #### Parameters
290
291 - `icmp_sock`
292
293 An ICMP socket. Once the wrapper is instantiated, this socket should no longer be used directly.
294
295 - Type: [`ICMPv4Socket`] or [`ICMPv6Socket`]
296
297 #### Methods
298
299 The same methods as for the underlying ICMP socket, except:
300
301 - `receive(request=None, timeout=2)`
302
303 This function is now non-blocking. It must be awaited.
304
305 - `detach()`
306
307 Detach the socket from the wrapper and return it. The wrapper cannot be used after this call but the socket can be reused for other purposes.
308
309 - `close()`
310
311 Detach the underlying socket from the wrapper and close it. Both cannot be used after this call.
312
313 #### Properties
314
315 The same properties as for the underlying ICMP socket, except:
316
317 - `is_closed`
318
319 Indicate whether the underlying socket is closed or detached from this wrapper.
320
321 - Type: `bool`
322
323 <br>
324
325 ### Examples
326
327 #### Sockets in action
328
329 ```python
330 from icmplib import *
331
332
333 def one_ping(address, timeout=2, id=PID):
334 # Create an ICMP socket
335 sock = ICMPv4Socket()
336
337 # Create an ICMP request
338 # See the 'ICMPRequest' class for details
339 request = ICMPRequest(
340 destination=address,
341 id=id,
342 sequence=1)
343
344 try:
345 sock.send(request)
346
347 # If the program arrives in this section, it means that the
348 # packet has been transmitted.
349
350 reply = sock.receive(request, timeout)
351
352 # If the program arrives in this section, it means that a
353 # packet has been received. The reply has the same identifier
354 # and sequence number that the request but it can come from
355 # an intermediate gateway.
356
357 reply.raise_for_status()
358
359 # If the program arrives in this section, it means that the
360 # destination host has responded to the request.
361
362 except TimeoutExceeded as err:
363 # The timeout has been reached
364 print(err)
365
366 except DestinationUnreachable as err:
367 # The reply indicates that the destination host is unreachable
368 print(err)
369
370 except TimeExceeded as err:
371 # The reply indicates that the time to live exceeded in transit
372 print(err)
373
374 except ICMPLibError as err:
375 # All other errors
376 print(err)
377
378 # Automatic socket closure (garbage collector)
379 ```
380
381 #### Verbose ping
382
383 ```python
384 from icmplib import *
385 from time import sleep
386
387
388 def verbose_ping(address, count=4, interval=1, timeout=2, id=PID):
389 # A payload of 56 bytes is used by default. You can modify it using
390 # the 'payload_size' parameter of your ICMP request.
391 print(f'PING {address}: 56 data bytes\n')
392
393 # We detect the socket to use from the specified IP address
394 if is_ipv6_address(address):
395 sock = ICMPv6Socket()
396 else:
397 sock = ICMPv4Socket()
398
399 for sequence in range(count):
400 # We create an ICMP request
401 request = ICMPRequest(
402 destination=address,
403 id=id,
404 sequence=sequence)
405
406 try:
407 # We send the request
408 sock.send(request)
409
410 # We are awaiting receipt of an ICMP reply
411 reply = sock.receive(request, timeout)
412
413 # We received a reply
414 # We display some information
415 print(f' {reply.bytes_received} bytes from '
416 f'{reply.source}: ', end='')
417
418 # We throw an exception if it is an ICMP error message
419 reply.raise_for_status()
420
421 # We calculate the round-trip time and we display it
422 round_trip_time = (reply.time - request.time) * 1000
423
424 print(f'icmp_seq={sequence} '
425 f'time={round(round_trip_time, 3)} ms')
426
427 # We wait before continuing
428 if sequence < count - 1:
429 sleep(interval)
430
431 except TimeoutExceeded:
432 # The timeout has been reached
433 print(f' Request timeout for icmp_seq {sequence}')
434
435 except ICMPError as err:
436 # An ICMP error message has been received
437 print(err)
438
439 except ICMPLibError:
440 # All other errors
441 print(' An error has occurred.')
442
443 print('\nCompleted.')
444
445
446 verbose_ping('1.1.1.1')
447
448 # PING 1.1.1.1: 56 data bytes
449 #
450 # 64 bytes from 1.1.1.1: icmp_seq=0 time=12.061 ms
451 # 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms
452 # 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms
453 # 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms
454 #
455 # Completed.
456 ```
457
458 [`ICMPRequest`]: #ICMPRequest
459 [`ICMPReply`]: #ICMPReply
460 [`ICMPv4Socket`]: #ICMPv4Socket
461 [`ICMPv6Socket`]: #ICMPv6Socket
462 [`ICMPSocketError`]: 4-exceptions.md#ICMPSocketError
463 [`SocketAddressError`]: 4-exceptions.md#SocketAddressError
464 [`SocketPermissionError`]: 4-exceptions.md#SocketPermissionError
465 [`SocketUnavailableError`]: 4-exceptions.md#SocketUnavailableError
466 [`SocketBroadcastError`]: 4-exceptions.md#SocketBroadcastError
467 [`TimeoutExceeded`]: 4-exceptions.md#TimeoutExceeded
468 [`ICMPError`]: 4-exceptions.md#ICMPError
469 [`DestinationUnreachable`]: 4-exceptions.md#DestinationUnreachable
470 [`TimeExceeded`]: 4-exceptions.md#TimeExceeded
0 # Exceptions
1
2 The library contains many exceptions to adapt to your needs:
3
4 ```
5 ICMPLibError
6 ├─ NameLookupError
7 ├─ ICMPSocketError
8 │ ├─ SocketAddressError
9 │ ├─ SocketPermissionError
10 │ ├─ SocketUnavailableError
11 │ ├─ SocketBroadcastError
12 │ └─ TimeoutExceeded
13
14 └─ ICMPError
15 ├─ DestinationUnreachable
16 │ ├─ ICMPv4DestinationUnreachable
17 │ └─ ICMPv6DestinationUnreachable
18
19 └─ TimeExceeded
20 ├─ ICMPv4TimeExceeded
21 └─ ICMPv6TimeExceeded
22 ```
23
24 ### ICMPLibError
25
26 Exception class for the icmplib package.
27
28 ### NameLookupError
29
30 Raised when the requested name does not exist or cannot be resolved. This concerns both Fully Qualified Domain Names and hostnames.
31
32 ### ICMPSocketError
33
34 Base class for ICMP sockets exceptions.
35
36 ### SocketAddressError
37
38 Raised when the requested address cannot be assigned to the socket.
39
40 ### SocketPermissionError
41
42 Raised when the privileges are insufficient to create the socket.
43
44 ### SocketUnavailableError
45
46 Raised when an action is performed while the socket is closed.
47
48 ### SocketBroadcastError
49
50 Raised when a broadcast address is used and the corresponding option is not enabled on the socket.
51
52 ### TimeoutExceeded
53
54 Raised when a timeout occurs on a socket.
55
56 ### ICMPError
57
58 Base class for ICMP error messages.
59
60 ### DestinationUnreachable
61
62 Base class for ICMP Destination Unreachable messages.
63
64 Destination Unreachable message is generated by the host or its inbound gateway to inform the client that the destination is unreachable for some reason.
65
66 ### TimeExceeded
67
68 Base class for ICMP Time Exceeded messages.
69
70 Time Exceeded message is generated by a gateway to inform the source of a discarded datagram due to the time to live field reaching zero. A Time Exceeded message may also be sent by a host if it fails to reassemble a fragmented datagram within its time limit.
0 # Utilities
1
2 ### resolve
3
4 Resolve a hostname or FQDN to an IP address. Depending on the name specified in parameters, several IP addresses may be returned.
5
6 This function relies on the DNS name server configured on your operating system.
7
8 ```python
9 resolve(name, family=None)
10 ```
11
12 #### Parameters
13
14 - `name`
15
16 A hostname or a Fully Qualified Domain Name (FQDN).
17
18 - Type: `str`
19
20 - `family`
21
22 The address family. Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default, this function searches for IPv4 addresses first for compatibility reasons (A DNS lookup) before searching for IPv6 addresses (AAAA DNS lookup).
23
24 - Type: `int`
25 - Default: `None`
26
27 #### Return value
28
29 - A list of IP addresses corresponding to the name passed as a parameter.
30
31 #### Exceptions
32
33 - [`NameLookupError`]
34
35 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
36
37 #### Example
38
39 ```python
40 >>> from icmplib import resolve
41
42 >>> resolve('one.one.one.one')
43 ['1.0.0.1', '1.1.1.1']
44
45 >>> resolve('localhost')
46 ['127.0.0.1']
47
48 >>> resolve('ipv6.google.com')
49 ['2a00:1450:4007:813::200e']
50 ```
51
52 <br>
53
54 ### async_resolve
55
56 Resolve a hostname or FQDN to an IP address. Depending on the name specified in parameters, several IP addresses may be returned.
57
58 This function relies on the DNS name server configured on your operating system.
59
60 *This function is non-blocking.*
61
62 ```python
63 async_resolve(name, family=None)
64 ```
65
66 #### Parameters, return value and exceptions
67
68 The same parameters, return values and exceptions as for the [`resolve`](#resolve) function.
69
70 #### Example
71
72 ```python
73 >>> import asyncio
74 >>> from icmplib import async_resolve
75
76 >>> asyncio.run(async_resolve('one.one.one.one'))
77 ['1.0.0.1', '1.1.1.1']
78
79 >>> asyncio.run(async_resolve('localhost'))
80 ['127.0.0.1']
81
82 >>> asyncio.run(async_resolve('ipv6.google.com'))
83 ['2a00:1450:4007:813::200e']
84 ```
85
86 <br>
87
88 ### is_hostname
89
90 Indicate whether the specified name is a hostname or an FQDN.
91
92 ```python
93 is_hostname(address)
94 ```
95
96 #### Example
97
98 ```python
99 >>> from icmplib import is_hostname
100
101 >>> is_hostname('one.one.one.one')
102 True
103
104 >>> is_hostname('localhost')
105 True
106
107 >>> is_hostname('127.0.0.1')
108 False
109
110 >>> is_hostname('2a00:1450:4007:813::200e')
111 False
112 ```
113
114 <br>
115
116 ### is_ipv4_address
117
118 Indicate whether the specified address is an IPv4 address.
119
120 This function does not perform a strict checking. Its does not check if each byte of the IP address is within the allowed range.
121
122 This function has been designed to be fast.
123
124 ```python
125 is_ipv4_address(address)
126 ```
127
128 #### Example
129
130 ```python
131 >>> from icmplib import is_ipv4_address
132
133 >>> is_ipv4_address('one.one.one.one')
134 False
135
136 >>> is_ipv4_address('localhost')
137 False
138
139 >>> is_ipv4_address('127.0.0.1')
140 True
141
142 >>> is_ipv4_address('2a00:1450:4007:813::200e')
143 False
144 ```
145
146 <br>
147
148 ### is_ipv6_address
149
150 Indicate whether the specified address is an IPv4 address.
151
152 This function does not perform a strict checking. Its does not check if each byte of the IP address is within the allowed range.
153
154 This function has been designed to be fast.
155
156 ```python
157 is_ipv6_address(address)
158 ```
159
160 #### Example
161
162 ```python
163 >>> from icmplib import is_ipv6_address
164
165 >>> is_ipv6_address('one.one.one.one')
166 False
167
168 >>> is_ipv6_address('localhost')
169 False
170
171 >>> is_ipv6_address('127.0.0.1')
172 False
173
174 >>> is_ipv6_address('2a00:1450:4007:813::200e')
175 True
176 ```
177
178 [`NameLookupError`]: 4-exceptions.md#NameLookupError
0 # Use icmplib without root privileges
1
2 - **Step 1: adapt your code**
3
4 To use icmplib without root privileges, you must set the `privileged` parameter to `False` on the [`ping`] and [`multiping`] functions, as well as their asynchronous variants and the low level classes.
5
6 By disabling this parameter, icmplib let the kernel handle some parts of the ICMP headers.
7
8 The [`traceroute`] function does not have this parameter. It should always be run as root to receive ICMP Time Exceeded messages from gateways.
9
10 - **Step 2: allow this feature on your operating system**
11
12 On some Linux systems, you must allow this feature:
13
14 ```shell
15 $ echo 'net.ipv4.ping_group_range = 0 2147483647' | sudo tee -a /etc/sysctl.conf
16 $ sudo sysctl -p
17 ```
18
19 You can check the current value with the following command:
20
21 ```shell
22 $ sysctl net.ipv4.ping_group_range
23 net.ipv4.ping_group_range = 0 2147483647
24 ```
25
26 *Since Ubuntu 20.04 LTS, this manipulation is no longer necessary.*
27
28 [Read more about `ping_group_range` on www.kernel.org]
29
30 [`ping`]: 2-functions.md#ping
31 [`multiping`]: 2-functions.md#multiping
32 [`traceroute`]: 2-functions.md#traceroute
33 [Read more about `ping_group_range` on www.kernel.org]: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
0 # Documentation
1
2 **[Installation](1-installation.md)**
3 - [Requirements](1-installation.md#requirements)
4 - [Installation and update](1-installation.md#installation-and-update)
5 - [Imports](1-installation.md#imports)
6
7 **[Built-in functions](2-functions.md)**
8 - [`ping`](2-functions.md#ping)
9 - [`multiping`](2-functions.md#multiping)
10 - [`traceroute`](2-functions.md#traceroute)
11 - [`async_ping`](2-functions.md#async_ping)
12 - [`async_multiping`](2-functions.md#async_multiping)
13
14 **[Sockets and classes](3-sockets.md)**
15 - [`ICMPRequest`](3-sockets.md#ICMPRequest)
16 - [`ICMPReply`](3-sockets.md#ICMPReply)
17 - [`ICMPv4Socket`](3-sockets.md#ICMPv4Socket)
18 - [`ICMPv6Socket`](3-sockets.md#ICMPv6Socket)
19 - [`AsyncSocket`](3-sockets.md#AsyncSocket)
20 - [Examples](3-sockets.md#examples)
21
22 **[Exceptions](4-exceptions.md)**
23 - [`ICMPLibError`](4-exceptions.md#ICMPLibError)
24 - [`NameLookupError`](4-exceptions.md#NameLookupError)
25 - [`ICMPSocketError`](4-exceptions.md#ICMPSocketError)
26 - [`SocketAddressError`](4-exceptions.md#SocketAddressError)
27 - [`SocketPermissionError`](4-exceptions.md#SocketPermissionError)
28 - [`SocketUnavailableError`](4-exceptions.md#SocketUnavailableError)
29 - [`SocketBroadcastError`](4-exceptions.md#SocketBroadcastError)
30 - [`TimeoutExceeded`](4-exceptions.md#TimeoutExceeded)
31 - [`ICMPError`](4-exceptions.md#ICMPError)
32 - [`DestinationUnreachable`](4-exceptions.md#DestinationUnreachable)
33 - [`TimeExceeded`](4-exceptions.md#TimeExceeded)
34
35 **[Utilities](5-utilities.md)**
36 - [`resolve`](5-utilities.md#resolve)
37 - [`async_resolve`](5-utilities.md#async_resolve)
38 - [`is_hostname`](5-utilities.md#is_hostname)
39 - [`is_ipv4_address`](5-utilities.md#is_ipv4_address)
40 - [`is_ipv6_address`](5-utilities.md#is_ipv6_address)
41
42 **[Use icmplib without root privileges](6-use-icmplib-without-privileges.md)**
43
44 **[Examples](../examples)**
2020 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms
2121 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms
2222 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms
23
24 Completed.
2325 ```
2426
2527 - [verbose-traceroute](verbose_traceroute.py)
3234 Traceroute to ovh.com (198.27.92.1): 56 data bytes, 30 hops max
3335
3436 1 192.168.0.254 192.168.0.254 9.86 ms
35 2 194.149.164.56 194.149.164.56 4.6 ms
36 3 213.186.32.181 be100-159.th2-1-a9.fr.eu 11.99 ms
37 4 94.23.122.146 be102.rbx-g1-nc5.fr.eu 7.81 ms
37 2 194.149.164.56 194.149.164.56 4.61 ms
38 3 213.186.32.181 be100-159.th2-1-a9.fr.eu 11.97 ms
39 4 94.23.122.146 be102.rbx-g1-nc5.fr.eu 15.81 ms
3840 5 * * *
39 6 37.187.231.75 be5.rbx-iplb1-a70.fr.eu 17.1 ms
41 6 37.187.231.75 be5.rbx-iplb1-a70.fr.eu 17.12 ms
4042 7 198.27.92.1 www.ovh.com 10.87 ms
43
44 Completed.
4145 ```
4246
4347 - [broadcast-ping](broadcast_ping.py)
6468 64 bytes from 10.0.0.17: icmp_seq=3 time=1.112 ms
6569 64 bytes from 10.0.0.40: icmp_seq=3 time=1.384 ms
6670 64 bytes from 10.0.0.41: icmp_seq=3 time=9.565 ms
71
72 Completed.
6773 ```
11 icmplib
22 ~~~~~~~
33
4 The power to forge ICMP packets and do ping and traceroute.
5
46 https://github.com/ValentinBELYN/icmplib
57
6 :copyright: Copyright 2017-2020 Valentin BELYN.
8 :copyright: Copyright 2017-2021 Valentin BELYN.
79 :license: GNU LGPLv3, see the LICENSE for details.
810
911 ~~~~~~~
1517 64 bytes from 10.0.0.17: icmp_seq=0 time=1.065 ms
1618 64 bytes from 10.0.0.40: icmp_seq=0 time=1.595 ms
1719 64 bytes from 10.0.0.41: icmp_seq=0 time=9.471 ms
18
1920 64 bytes from 10.0.0.17: icmp_seq=1 time=0.983 ms
2021 64 bytes from 10.0.0.40: icmp_seq=1 time=1.579 ms
2122 64 bytes from 10.0.0.41: icmp_seq=1 time=9.345 ms
22
2323 64 bytes from 10.0.0.17: icmp_seq=2 time=0.916 ms
2424 64 bytes from 10.0.0.40: icmp_seq=2 time=2.031 ms
2525 64 bytes from 10.0.0.41: icmp_seq=2 time=9.554 ms
26
2726 64 bytes from 10.0.0.17: icmp_seq=3 time=1.112 ms
2827 64 bytes from 10.0.0.40: icmp_seq=3 time=1.384 ms
2928 64 bytes from 10.0.0.41: icmp_seq=3 time=9.565 ms
29
30 Completed.
3031 '''
3132
32 from icmplib import (
33 ICMPv4Socket,
34 ICMPRequest,
35 TimeoutExceeded,
36 ICMPLibError,
37 PID)
33 from icmplib import ICMPv4Socket, ICMPRequest
34 from icmplib import ICMPLibError, TimeoutExceeded, PID
3835
3936
4037 def broadcast_ping(address, count=4, timeout=1, id=PID):
41 # ICMPRequest uses a payload of 56 bytes by default
42 # You can modify it using the payload_size parameter
43 print(f'PING {address}: 56 data bytes')
38 # A payload of 56 bytes is used by default. You can modify it using
39 # the 'payload_size' parameter of your ICMP request.
40 print(f'PING {address}: 56 data bytes\n')
4441
4542 # Broadcast is only possible in IPv4
46 socket = ICMPv4Socket()
43 sock = ICMPv4Socket()
4744
4845 # We allow the socket to send broadcast packets
49 socket.broadcast = True
46 sock.broadcast = True
5047
5148 for sequence in range(count):
5249 # We create an ICMP request
5350 request = ICMPRequest(
5451 destination=address,
5552 id=id,
56 sequence=sequence,
57 timeout=timeout)
58
59 print()
53 sequence=sequence)
6054
6155 try:
6256 # We send the request
63 socket.send(request)
57 sock.send(request)
6458
6559 while 'we receive replies':
66 # We are awaiting receipt of an ICMP reply
67 # If there is no more responses, the TimeoutExceeded
68 # exception is thrown and the loop is stopped
69 reply = socket.receive()
60 # We are awaiting receipt of an ICMP reply. If there is
61 # no more responses, the 'TimeoutExceeded' exception is
62 # thrown and the loop is stopped.
63 reply = sock.receive(request, timeout)
7064
71 # We calculate the round-trip time of the reply
65 # We calculate the round-trip time
7266 round_trip_time = (reply.time - request.time) * 1000
7367
7468 # We display some information
8377
8478 except ICMPLibError:
8579 # All other errors
86 print('An error has occurred.')
80 print(' An error has occurred.')
81
82 print('\nCompleted.')
8783
8884
8985 # Limited broadcast
11 icmplib
22 ~~~~~~~
33
4 The power to forge ICMP packets and do ping and traceroute.
5
46 https://github.com/ValentinBELYN/icmplib
57
6 :copyright: Copyright 2017-2020 Valentin BELYN.
8 :copyright: Copyright 2017-2021 Valentin BELYN.
79 :license: GNU LGPLv3, see the LICENSE for details.
810
911 ~~~~~~~
1113 Example: multiping
1214 '''
1315
14 from icmplib import multiping
16 from icmplib import resolve, multiping
1517
1618
1719 addresses = [
18 # A fully qualified domain name (FQDN) is allowed. The first
19 # address returned from the DNS resolution will be used.
20 # For deterministic behavior, prefer to use an IP address.
21 'github.com',
22
2320 # IPv4 addresses
2421 '1.1.1.1',
2522 '8.8.8.8',
2825
2926 # IPv6 addresses
3027 '::1',
28
29 # Hostnames and Fully Qualified Domain Names (FQDNs) are allowed but
30 # not recommended. You can easily retrieve their IP address by
31 # calling the built-in 'resolve' function. For deterministic
32 # behavior, prefer to use an IP address.
33 'github.com'
3134 ]
3235
33 hosts = multiping(addresses, count=2, interval=0.5, timeout=1)
36 hosts = multiping(addresses, count=2, timeout=1)
3437
3538 hosts_alive = []
3639 hosts_dead = []
3841 for host in hosts:
3942 if host.is_alive:
4043 hosts_alive.append(host.address)
41
4244 else:
4345 hosts_dead.append(host.address)
4446
4547 print(hosts_alive)
46 # ['github.com', '1.1.1.1', '8.8.8.8', '::1']
48 # ['1.1.1.1', '8.8.8.8', '::1', '140.82.121.4']
4749
4850 print(hosts_dead)
4951 # ['10.0.0.100', '10.0.0.200']
11 icmplib
22 ~~~~~~~
33
4 The power to forge ICMP packets and do ping and traceroute.
5
46 https://github.com/ValentinBELYN/icmplib
57
6 :copyright: Copyright 2017-2020 Valentin BELYN.
8 :copyright: Copyright 2017-2021 Valentin BELYN.
79 :license: GNU LGPLv3, see the LICENSE for details.
810
911 ~~~~~~~
1618
1719 host = ping('1.1.1.1', count=10, interval=0.5, timeout=1)
1820
19 # The IP address of the gateway or host that responded to the request
21 # The IP address of the host that responded to the request
2022 print(host.address)
2123 # '1.1.1.1'
2224
23 # The minimum round-trip time
25 # The minimum round-trip time in milliseconds
2426 print(host.min_rtt)
25 # 12.2
27 # 5.761
2628
27 # The average round-trip time
29 # The average round-trip time in milliseconds
2830 print(host.avg_rtt)
29 # 13.2
31 # 12.036
3032
31 # The maximum round-trip time
33 # The maximum round-trip time in milliseconds
3234 print(host.max_rtt)
33 # 17.6
35 # 16.207
3436
35 # The number of packets transmitted to the destination host
37 # The list of round-trip times expressed in milliseconds
38 print(host.rtts)
39 # [ 11.595010757446289, 13.135194778442383, 9.614229202270508,
40 # 16.018152236938477, 11.960029602050781, 5.761146545410156,
41 # 16.207218170166016, 11.937141418457031, 12.098073959350586 ]
42
43 # The number of requests transmitted to the remote host
3644 print(host.packets_sent)
3745 # 10
3846
39 # The number of packets sent by the remote host and received by the
40 # current host
47 # The number of ICMP responses received from the remote host
4148 print(host.packets_received)
4249 # 9
4350
4653 print(host.packet_loss)
4754 # 0.1
4855
56 # The jitter in milliseconds, defined as the variance of the latency of
57 # packets flowing through the network
58 # At least two ICMP responses are required to calculate the jitter
59 print(host.jitter)
60 # 4.575
61
4962 # Indicates whether the host is reachable
5063 print(host.is_alive)
5164 # True
11 icmplib
22 ~~~~~~~
33
4 The power to forge ICMP packets and do ping and traceroute.
5
46 https://github.com/ValentinBELYN/icmplib
57
6 :copyright: Copyright 2017-2020 Valentin BELYN.
8 :copyright: Copyright 2017-2021 Valentin BELYN.
79 :license: GNU LGPLv3, see the LICENSE for details.
810
911 ~~~~~~~
1416 from icmplib import traceroute
1517
1618
17 hops = traceroute('1.1.1.1', timeout=1, fast_mode=True)
19 hops = traceroute('1.1.1.1', timeout=1, fast=True)
1820
1921 print(hops)
20 # [<Hop 1 [192.168.0.254]>, <Hop 2 [194.149.169.162]>,
21 # <Hop 4 [149.11.115.13]>, <Hop 5 [154.54.61.21]>,
22 # <Hop 6 [154.54.60.126]>, <Hop 7 [149.11.0.126]>,
23 # <Hop 8 [1.1.1.1]>]
24
22 # [ <Hop 1 [10.0.0.1]>, <Hop 2 [194.149.169.49]>,
23 # <Hop 3 [194.149.166.54]>, <Hop 5 [212.73.205.22]>,
24 # <Hop 6 [1.1.1.1]> ]
2525
2626 last_distance = 0
2727
2828 for hop in hops:
2929 if last_distance + 1 != hop.distance:
30 print(' * Some routers are not responding')
30 print(' * Some gateways are not responding')
3131
32 print(f'{hop.distance:4} {hop.address:15} '
32 print(f' {hop.distance:<2} {hop.address:15} '
3333 f'{hop.avg_rtt} ms')
3434
3535 last_distance = hop.distance
3636
37 # 1 192.168.0.254 11.327 ms
38 # 2 194.149.169.162 16.354 ms
39 # * Some routers are not responding
40 # 4 149.11.115.13 11.498 ms
41 # 5 154.54.61.21 4.335 ms
42 # 6 154.54.60.126 5.645 ms
43 # 7 149.11.0.126 5.873 ms
44 # 8 1.1.1.1 4.561 ms
37 # 1 10.0.0.1 5.196 ms
38 # 2 194.149.169.49 7.552 ms
39 # 3 194.149.166.54 12.21 ms
40 # * Some gateways are not responding
41 # 5 212.73.205.22 22.15 ms
42 # 6 1.1.1.1 13.59 ms
11 icmplib
22 ~~~~~~~
33
4 The power to forge ICMP packets and do ping and traceroute.
5
46 https://github.com/ValentinBELYN/icmplib
57
6 :copyright: Copyright 2017-2020 Valentin BELYN.
8 :copyright: Copyright 2017-2021 Valentin BELYN.
79 :license: GNU LGPLv3, see the LICENSE for details.
810
911 ~~~~~~~
1618 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms
1719 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms
1820 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms
21
22 Completed.
1923 '''
20
21 from icmplib import (
22 ICMPv4Socket,
23 ICMPv6Socket,
24 ICMPRequest,
25 TimeoutExceeded,
26 ICMPError,
27 ICMPLibError,
28 is_ipv6_address,
29 PID)
3024
3125 from time import sleep
3226
27 from icmplib import ICMPv4Socket, ICMPv6Socket, ICMPRequest
28 from icmplib import ICMPLibError, ICMPError, TimeoutExceeded
29 from icmplib import PID, is_ipv6_address
30
3331
3432 def verbose_ping(address, count=4, interval=1, timeout=2, id=PID):
35 # ICMPRequest uses a payload of 56 bytes by default
36 # You can modify it using the payload_size parameter
33 # A payload of 56 bytes is used by default. You can modify it using
34 # the 'payload_size' parameter of your ICMP request.
3735 print(f'PING {address}: 56 data bytes\n')
3836
39 # Detection of the socket to use
37 # We detect the socket to use from the specified IP address
4038 if is_ipv6_address(address):
41 socket = ICMPv6Socket()
42
39 sock = ICMPv6Socket()
4340 else:
44 socket = ICMPv4Socket()
41 sock = ICMPv4Socket()
4542
4643 for sequence in range(count):
4744 # We create an ICMP request
4845 request = ICMPRequest(
4946 destination=address,
5047 id=id,
51 sequence=sequence,
52 timeout=timeout)
48 sequence=sequence)
5349
5450 try:
5551 # We send the request
56 socket.send(request)
52 sock.send(request)
5753
5854 # We are awaiting receipt of an ICMP reply
59 reply = socket.receive()
55 reply = sock.receive(request, timeout)
6056
6157 # We received a reply
6258 # We display some information
7268 print(f'icmp_seq={sequence} '
7369 f'time={round(round_trip_time, 3)} ms')
7470
75 # We pause before continuing
71 # We wait before continuing
7672 if sequence < count - 1:
7773 sleep(interval)
7874
7975 except TimeoutExceeded:
8076 # The timeout has been reached
81 print(f'Request timeout for icmp_seq {sequence}')
77 print(f' Request timeout for icmp_seq {sequence}')
8278
8379 except ICMPError as err:
8480 # An ICMP error message has been received
8682
8783 except ICMPLibError:
8884 # All other errors
89 print('An error has occurred.')
85 print(' An error has occurred.')
86
87 print('\nCompleted.')
9088
9189
9290 verbose_ping('1.1.1.1')
11 icmplib
22 ~~~~~~~
33
4 The power to forge ICMP packets and do ping and traceroute.
5
46 https://github.com/ValentinBELYN/icmplib
57
6 :copyright: Copyright 2017-2020 Valentin BELYN.
8 :copyright: Copyright 2017-2021 Valentin BELYN.
79 :license: GNU LGPLv3, see the LICENSE for details.
810
911 ~~~~~~~
1315 Traceroute to ovh.com (198.27.92.1): 56 data bytes, 30 hops max
1416
1517 1 192.168.0.254 192.168.0.254 9.86 ms
16 2 194.149.164.56 194.149.164.56 4.6 ms
17 3 213.186.32.181 be100-159.th2-1-a9.fr.eu 11.99 ms
18 4 94.23.122.146 be102.rbx-g1-nc5.fr.eu 7.81 ms
18 2 194.149.164.56 194.149.164.56 4.61 ms
19 3 213.186.32.181 be100-159.th2-1-a9.fr.eu 11.97 ms
20 4 94.23.122.146 be102.rbx-g1-nc5.fr.eu 15.81 ms
1921 5 * * *
20 6 37.187.231.75 be5.rbx-iplb1-a70.fr.eu 17.1 ms
22 6 37.187.231.75 be5.rbx-iplb1-a70.fr.eu 17.12 ms
2123 7 198.27.92.1 www.ovh.com 10.87 ms
24
25 Completed.
2226 '''
2327
24 from icmplib import (
25 ICMPv4Socket,
26 ICMPv6Socket,
27 ICMPRequest,
28 TimeoutExceeded,
29 TimeExceeded,
30 ICMPLibError,
31 is_ipv6_address,
32 PID)
33
34 from socket import getfqdn, gethostbyname
28 from socket import getfqdn
3529 from time import sleep
3630
31 from icmplib import ICMPv4Socket, ICMPv6Socket, ICMPRequest
32 from icmplib import ICMPLibError, TimeoutExceeded, TimeExceeded
33 from icmplib import PID, resolve, is_hostname, is_ipv6_address
3734
38 def verbose_traceroute(address, count=3, interval=0.05, timeout=2,
35
36 def verbose_traceroute(address, count=2, interval=0.05, timeout=2,
3937 id=PID, max_hops=30):
40 # ICMPRequest uses a payload of 56 bytes by default
41 # You can modify it using the payload_size parameter
42 print(f'Traceroute to {address} ({gethostbyname(address)}): '
38 # We perform a DNS lookup if a hostname or an FQDN is passed in
39 # parameters.
40 if is_hostname(address):
41 ip_address = resolve(address)[0]
42 else:
43 ip_address = address
44
45 # A payload of 56 bytes is used by default. You can modify it using
46 # the 'payload_size' parameter of your ICMP request.
47 print(f'Traceroute to {address} ({ip_address}): '
4348 f'56 data bytes, {max_hops} hops max\n')
4449
45 # Detection of the socket to use
46 if is_ipv6_address(address):
47 socket = ICMPv6Socket()
48
50 # We detect the socket to use from the specified IP address
51 if is_ipv6_address(ip_address):
52 sock = ICMPv6Socket()
4953 else:
50 socket = ICMPv4Socket()
54 sock = ICMPv4Socket()
5155
5256 ttl = 1
5357 host_reached = False
5660 for sequence in range(count):
5761 # We create an ICMP request
5862 request = ICMPRequest(
59 destination=address,
63 destination=ip_address,
6064 id=id,
6165 sequence=sequence,
62 timeout=timeout,
6366 ttl=ttl)
6467
6568 try:
6669 # We send the request
67 socket.send(request)
70 sock.send(request)
6871
6972 # We are awaiting receipt of an ICMP reply
70 reply = socket.receive()
73 reply = sock.receive(request, timeout)
7174
7275 # We received a reply
7376 # We display some information
7477 source_name = getfqdn(reply.source)
7578
76 print(f'{ttl:3} {reply.source:15} '
79 print(f' {ttl:<2} {reply.source:15} '
7780 f'{source_name:40} ', end='')
7881
7982 # We throw an exception if it is an ICMP error message
103106
104107 except TimeoutExceeded:
105108 # The timeout has been reached and no host or gateway
106 # has responded after multiple attemps
109 # has responded after multiple attempts
107110 if sequence >= count - 1:
108 print(f'{ttl:3} * * *')
111 print(f' {ttl:<2} * * *')
109112
110113 except ICMPLibError:
111114 # Other errors are ignored
113116
114117 ttl += 1
115118
116 print()
119 print('\nCompleted.')
117120
118121
122 # This function supports both FQDNs and IP addresses. See the 'resolve'
123 # function for details.
119124 verbose_traceroute('ovh.com')
11 icmplib
22 ~~~~~~~
33
4 The power to forge ICMP packets and do ping and traceroute.
5
46 https://github.com/ValentinBELYN/icmplib
57
6 :copyright: Copyright 2017-2020 Valentin BELYN.
8 :copyright: Copyright 2017-2021 Valentin BELYN.
79 :license: GNU LGPLv3, see the LICENSE for details.
810
911 ~~~~~~~
2325 <https://www.gnu.org/licenses/>.
2426 '''
2527
26 from .sockets import ICMPv4Socket, ICMPv6Socket
28 from .sockets import ICMPv4Socket, ICMPv6Socket, AsyncSocket
2729 from .models import ICMPRequest, ICMPReply, Host, Hop
28 from .ping import ping, multiping
30 from .ping import ping, async_ping
31 from .multiping import multiping, async_multiping
2932 from .traceroute import traceroute
3033 from .exceptions import *
31 from .utils import PID, is_ipv4_address, is_ipv6_address
34 from .utils import is_hostname, is_ipv4_address, is_ipv6_address
35 from .utils import PID, resolve, async_resolve
3236
3337
3438 __author__ = 'Valentin BELYN'
35 __copyright__ = 'Copyright 2017-2020 Valentin BELYN'
39 __copyright__ = 'Copyright 2017-2021 Valentin BELYN'
3640 __license__ = 'GNU Lesser General Public License v3.0'
3741
38 __version__ = '1.2.2'
39 __build__ = '201010'
42 __version__ = '3.0.2'
43 __build__ = '211031'
11 icmplib
22 ~~~~~~~
33
4 The power to forge ICMP packets and do ping and traceroute.
5
46 https://github.com/ValentinBELYN/icmplib
57
6 :copyright: Copyright 2017-2020 Valentin BELYN.
8 :copyright: Copyright 2017-2021 Valentin BELYN.
79 :license: GNU LGPLv3, see the LICENSE for details.
810
911 ~~~~~~~
2931 Exception class for the icmplib package.
3032
3133 '''
32 def __init__(self, message):
33 self._message = message
34
35 def __str__(self):
36 return self._message
37
38 @property
39 def message(self):
40 return self._message
34
35
36 class NameLookupError(ICMPLibError):
37 '''
38 Raised when the requested name does not exist or cannot be resolved.
39 This concerns both Fully Qualified Domain Names and hostnames.
40
41 '''
42 def __init__(self, name):
43 message = f'The name \'{name}\' cannot be resolved'
44 super().__init__(message)
4145
4246
4347 class ICMPSocketError(ICMPLibError):
4751 '''
4852
4953
54 class SocketAddressError(ICMPSocketError):
55 '''
56 Raised when the requested address cannot be assigned to the socket.
57
58 '''
59 def __init__(self, address):
60 message = f'The requested address ({address}) cannot be ' \
61 'assigned to the socket'
62 super().__init__(message)
63
64
5065 class SocketPermissionError(ICMPSocketError):
5166 '''
52 Raised when the permissions are insufficient to create a socket.
53
54 '''
55 def __init__(self):
56 message = 'Root privileges are required to create the socket'
67 Raised when the privileges are insufficient to create the socket.
68
69 '''
70 def __init__(self, privileged):
71 if privileged:
72 message = 'Root privileges are required to create the socket'
73 else:
74 message = 'A prior configuration of your OS is required ' \
75 'to use ICMP sockets without root privileges. ' \
76 'Read more on https://github.com/ValentinBELYN' \
77 '/icmplib'
78
5779 super().__init__(message)
5880
5981
6991
7092 class SocketBroadcastError(ICMPSocketError):
7193 '''
72 Raised when a broadcast address is used and the corresponding
73 option is not enabled on the socket.
94 Raised when a broadcast address is used and the corresponding option
95 is not enabled on the socket.
7496
7597 '''
7698 def __init__(self):
7799 message = 'Broadcast is not allowed: ' \
78 'please use broadcast method (setter) to allow it'
100 'please use the \'broadcast\' property to allow it'
79101 super().__init__(message)
80102
81103
117139 def __init__(self, reply):
118140 if reply.code in self._CODES:
119141 message = self._CODES[reply.code]
120
121142 else:
122143 message = f'Destination unreachable, bad code: {reply.code}'
123144
161182 '''
162183 Base class for ICMP Time Exceeded messages.
163184
164 Time Exceeded message is generated by a gateway to inform the
165 source of a discarded datagram due to the time to live field
166 reaching zero. A Time Exceeded message may also be sent by a host
167 if it fails to reassemble a fragmented datagram within its time
168 limit.
185 Time Exceeded message is generated by a gateway to inform the source
186 of a discarded datagram due to the time to live field reaching zero.
187 A Time Exceeded message may also be sent by a host if it fails to
188 reassemble a fragmented datagram within its time limit.
169189
170190 '''
171191 _CODES = {}
173193 def __init__(self, reply):
174194 if reply.code in self._CODES:
175195 message = self._CODES[reply.code]
176
177196 else:
178197 message = f'Time exceeded, bad code: {reply.code}'
179198
+0
-152
icmplib/ip.py less more
0 '''
1 icmplib
2 ~~~~~~~
3
4 https://github.com/ValentinBELYN/icmplib
5
6 :copyright: Copyright 2017-2020 Valentin BELYN.
7 :license: GNU LGPLv3, see the LICENSE for details.
8
9 ~~~~~~~
10
11 This program is free software: you can redistribute it and/or
12 modify it under the terms of the GNU Lesser General Public License
13 as published by the Free Software Foundation, either version 3 of
14 the License, or (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU Lesser General Public License for more details.
20
21 You should have received a copy of the GNU Lesser General Public
22 License along with this program. If not, see
23 <https://www.gnu.org/licenses/>.
24 '''
25
26 import socket
27 from .utils import PLATFORM_WINDOWS
28
29
30 # Fix for Windows
31 if PLATFORM_WINDOWS:
32 socket.IPPROTO_IPV6 = 41
33
34
35 class IPSocket:
36 def __init__(self, family, protocol):
37 self._socket = socket.socket(
38 family=family,
39 type=socket.SOCK_RAW,
40 proto=protocol)
41
42 self.timeout = 5
43 self.ttl = 64
44 self.traffic_class = 0
45
46 def send(self, payload, address, port):
47 return self._socket.sendto(payload, (address, port))
48
49 def receive(self, buffer_size=1024):
50 packet = self._socket.recvfrom(buffer_size)
51
52 payload = packet[0]
53 address = packet[1][0]
54 port = packet[1][1]
55
56 return payload, address, port
57
58 def close(self):
59 self._socket.close()
60
61 @property
62 def timeout(self):
63 return self._timeout
64
65 @timeout.setter
66 def timeout(self, timeout):
67 self._socket.settimeout(timeout)
68 self._timeout = timeout
69
70 @property
71 def ttl(self):
72 return self._ttl
73
74 @ttl.setter
75 def ttl(self, ttl):
76 self._ttl = ttl
77
78 @property
79 def traffic_class(self):
80 return self._traffic_class
81
82 @traffic_class.setter
83 def traffic_class(self, traffic_class):
84 self._traffic_class = traffic_class
85
86
87 class IPv4Socket(IPSocket):
88 def __init__(self, protocol):
89 super().__init__(
90 family=socket.AF_INET,
91 protocol=protocol)
92
93 self._broadcast = False
94
95 @IPSocket.ttl.setter
96 def ttl(self, ttl):
97 self._ttl = ttl
98
99 self._socket.setsockopt(
100 socket.IPPROTO_IP,
101 socket.IP_TTL,
102 ttl)
103
104 @IPSocket.traffic_class.setter
105 def traffic_class(self, traffic_class):
106 self._traffic_class = traffic_class
107
108 if not PLATFORM_WINDOWS:
109 self._socket.setsockopt(
110 socket.IPPROTO_IP,
111 socket.IP_TOS,
112 traffic_class)
113
114 @property
115 def broadcast(self):
116 return self._broadcast
117
118 @broadcast.setter
119 def broadcast(self, allow):
120 self._broadcast = allow
121
122 self._socket.setsockopt(
123 socket.SOL_SOCKET,
124 socket.SO_BROADCAST,
125 allow)
126
127
128 class IPv6Socket(IPSocket):
129 def __init__(self, protocol):
130 super().__init__(
131 family=socket.AF_INET6,
132 protocol=protocol)
133
134 @IPSocket.ttl.setter
135 def ttl(self, ttl):
136 self._ttl = ttl
137
138 self._socket.setsockopt(
139 socket.IPPROTO_IPV6,
140 socket.IPV6_MULTICAST_HOPS,
141 ttl)
142
143 @IPSocket.traffic_class.setter
144 def traffic_class(self, traffic_class):
145 self._traffic_class = traffic_class
146
147 if not PLATFORM_WINDOWS:
148 self._socket.setsockopt(
149 socket.IPPROTO_IPV6,
150 socket.IPV6_TCLASS,
151 traffic_class)
11 icmplib
22 ~~~~~~~
33
4 The power to forge ICMP packets and do ping and traceroute.
5
46 https://github.com/ValentinBELYN/icmplib
57
6 :copyright: Copyright 2017-2020 Valentin BELYN.
8 :copyright: Copyright 2017-2021 Valentin BELYN.
79 :license: GNU LGPLv3, see the LICENSE for details.
810
911 ~~~~~~~
2426 '''
2527
2628 from .exceptions import *
27 from .utils import is_ipv6_address
29 from .utils import random_byte_message
2830
2931
3032 class ICMPRequest:
3133 '''
32 A user-created object that represents an ICMP ECHO_REQUEST.
34 A user-created object that represents an ICMP Echo Request.
3335
3436 :type destination: str
35 :param destination: The IP address of the gateway or host to which
36 the message should be sent.
37 :param destination: The IP address of the host to which the message
38 should be sent.
3739
3840 :type id: int
3941 :param id: The identifier of the request. Used to match the reply
4042 with the request. In practice, a unique identifier is used for
41 every ping process.
43 every ping process. On Linux, this identifier is automatically
44 replaced if the request is sent from an unprivileged socket.
4245
4346 :type sequence: int
4447 :param sequence: The sequence number. Used to match the reply with
4548 the request. Typically, the sequence number is incremented for
4649 each packet sent during the process.
4750
48 :type payload: bytes
49 :param payload: (Optional) The payload content in bytes. A random
50 payload is used by default.
51
52 :type payload_size: int
53 :param payload_size: (Optional) The payload size. Ignored when the
54 `payload` parameter is set.
55
56 :type timeout: int or float
57 :param timeout: (Optional) The maximum waiting time for receiving
58 the reply in seconds.
59
60 :type ttl: int
61 :param ttl: (Optional) The time to live of the packet in seconds.
62
63 :type traffic_class: int
64 :param traffic_class: (Optional) The traffic class of the packet.
65 Provides a defined level of service to the packet by setting
66 the DS Field (formerly TOS) or the Traffic Class field of the
67 IP header. Packets are delivered with the minimum priority by
51 :type payload: bytes, optional
52 :param payload: The payload content in bytes. A random payload is
53 used by default.
54
55 :type payload_size: int, optional
56 :param payload_size: The payload size. Ignored when the `payload`
57 parameter is set. Default to 56.
58
59 :type ttl: int, optional
60 :param ttl: The time to live of the packet in terms of hops.
61 Default to 64.
62
63 :type traffic_class: int, optional
64 :param traffic_class: The traffic class of the ICMP packet.
65 Provides a defined level of service to the packet by setting the
66 DS Field (formerly TOS) or the Traffic Class field of the IP
67 header. Packets are delivered with the minimum priority by
6868 default (Best-effort delivery).
6969 Intermediate routers must be able to support this feature.
7070 Only available on Unix systems. Ignored on Windows.
7171
7272 '''
73 __slots__ = '_destination', '_id', '_sequence', '_payload', \
74 '_payload_size', '_ttl', '_traffic_class', '_time'
75
7376 def __init__(self, destination, id, sequence, payload=None,
74 payload_size=56, timeout=2, ttl=64, traffic_class=0):
75
76 id &= 0xffff
77 sequence &= 0xffff
77 payload_size=56, ttl=64, traffic_class=0):
7878
7979 if payload:
8080 payload_size = len(payload)
8181
8282 self._destination = destination
83 self._id = id
84 self._sequence = sequence
83 self._id = id & 0xffff
84 self._sequence = sequence & 0xffff
8585 self._payload = payload
8686 self._payload_size = payload_size
87 self._timeout = timeout
8887 self._ttl = ttl
8988 self._traffic_class = traffic_class
9089 self._time = 0
9594 @property
9695 def destination(self):
9796 '''
98 The IP address of the gateway or host to which the message
99 should be sent.
97 The IP address of the host to which the message should be sent.
10098
10199 '''
102100 return self._destination
123121 def payload(self):
124122 '''
125123 The payload content in bytes.
126 Return `None` if the payload is random.
127
128 '''
129 return self._payload
124 Return a random payload if not defined.
125
126 '''
127 return self._payload or random_byte_message(self._payload_size)
130128
131129 @property
132130 def payload_size(self):
137135 return self._payload_size
138136
139137 @property
140 def timeout(self):
141 '''
142 The maximum waiting time for receiving the reply in seconds.
143
144 '''
145 return self._timeout
146
147 @property
148138 def ttl(self):
149139 '''
150 The time to live of the packet in seconds.
140 The time to live of the packet in terms of hops.
151141
152142 '''
153143 return self._ttl
164154 def time(self):
165155 '''
166156 The timestamp of the ICMP request.
157
167158 Initialized to zero when creating the request and replaced by
168 `ICMPv4Socket` or `ICMPv6Socket` with the time of sending.
159 the `send` method of an ICMP socket with the time of sending.
169160
170161 '''
171162 return self._time
173164
174165 class ICMPReply:
175166 '''
176 A class that represents an ICMP reply. Generated from an
177 `ICMPSocket` object (`ICMPv4Socket` or `ICMPv6Socket`).
167 A class that represents an ICMP reply. Generated from an ICMP socket.
178168
179169 :type source: str
180 :param source: The IP address of the gateway or host that composes
181 the ICMP message.
170 :param source: The IP address of the host that composes the ICMP
171 message.
172
173 :type family: int
174 :param family: The address family. Can be set to `4` for IPv4 or `6`
175 for IPv6 addresses.
182176
183177 :type id: int
184 :param id: The identifier of the reply. Used to match the reply
185 with the request.
178 :param id: The identifier of the reply. Used to match the reply with
179 the request.
186180
187181 :type sequence: int
188182 :param sequence: The sequence number. Used to match the reply with
189183 the request.
190184
191185 :type type: int
192 :param type: The type of message.
186 :param type: The type of ICMP message.
193187
194188 :type code: int
195 :param code: The error code.
189 :param code: The ICMP error code.
196190
197191 :type bytes_received: int
198192 :param bytes_received: The number of bytes received.
201195 :param time: The timestamp of the ICMP reply.
202196
203197 '''
204 def __init__(self, source, id, sequence, type, code,
198 __slots__ = '_source', '_family', '_id', '_sequence', '_type', \
199 '_code', '_bytes_received', '_time'
200
201 def __init__(self, source, family, id, sequence, type, code,
205202 bytes_received, time):
206203
207204 self._source = source
205 self._family = family
208206 self._id = id
209207 self._sequence = sequence
210208 self._type = type
217215
218216 def raise_for_status(self):
219217 '''
220 Throw an exception if the reply is not an ICMP ECHO_REPLY.
218 Throw an exception if the reply is not an ICMP Echo Reply.
221219 Otherwise, do nothing.
222220
223 :raises ICMPv4DestinationUnreachable: If the ICMPv4 reply is
224 type 3.
225 :raises ICMPv4TimeExceeded: If the ICMPv4 reply is type 11.
226 :raises ICMPv6DestinationUnreachable: If the ICMPv6 reply is
227 type 1.
228 :raises ICMPv6TimeExceeded: If the ICMPv6 reply is type 3.
229 :raises ICMPError: If the reply is of another type and is not
230 an ICMP ECHO_REPLY.
231
232 '''
233 if is_ipv6_address(self._source):
234 echo_reply_type = 129
235 errors = {
236 1: ICMPv6DestinationUnreachable,
237 3: ICMPv6TimeExceeded
238 }
239
221 :raises DestinationUnreachable: If the destination is
222 unreachable for some reason.
223 :raises TimeExceeded: If the time to live field of the ICMP
224 request has reached zero.
225 :raises ICMPError: Raised for any other type and ICMP error
226 code, except ICMP Echo Reply messages.
227
228 '''
229 if self._family == 6:
230 if self._type == 1:
231 raise ICMPv6DestinationUnreachable(self)
232
233 if self._type == 3:
234 raise ICMPv6TimeExceeded(self)
240235 else:
241 echo_reply_type = 0
242 errors = {
243 3: ICMPv4DestinationUnreachable,
244 11: ICMPv4TimeExceeded
245 }
246
247 if self._type in errors:
248 raise errors[self._type](self)
249
250 if self._type != echo_reply_type:
251 message = f'Error type: {self._type}, ' \
252 f'code: {self._code}'
253
236 if self._type == 3:
237 raise ICMPv4DestinationUnreachable(self)
238
239 if self._type == 11:
240 raise ICMPv4TimeExceeded(self)
241
242 if (self._family == 4 and self._type != 0 or
243 self._family == 6 and self._type != 129):
244 message = f'Error type: {self._type}, code: {self._code}'
254245 raise ICMPError(message, self)
255246
256247 @property
257248 def source(self):
258249 '''
259 The IP address of the gateway or host that composes the ICMP
260 message.
250 The IP address of the host that composes the ICMP message.
261251
262252 '''
263253 return self._source
265255 @property
266256 def id(self):
267257 '''
268 The identifier of the request.
258 The identifier of the reply.
269259 Used to match the reply with the request.
270260
271261 '''
283273 @property
284274 def type(self):
285275 '''
286 The type of message.
276 The type of ICMP message.
287277
288278 '''
289279 return self._type
291281 @property
292282 def code(self):
293283 '''
294 The error code.
284 The ICMP error code.
295285
296286 '''
297287 return self._code
305295 return self._bytes_received
306296
307297 @property
308 def received_bytes(self):
309 '''
310 Deprecated: use the `bytes_received` property instead.
311
312 '''
313 print('[icmplib] Deprecation Warning: The `received_bytes` '
314 'property will be removed from icmplib 2.0. Use the '
315 '`bytes_received` property instead.')
316
317 return self._bytes_received
318
319 @property
320298 def time(self):
321299 '''
322300 The timestamp of the ICMP reply.
327305
328306 class Host:
329307 '''
330 A class that represents a host. Simplifies the exploitation of
331 results from `ping` and `traceroute` functions.
308 A class that represents a host. It simplifies the use of the results
309 from the `ping`, `multiping` and `traceroute` functions.
332310
333311 :type address: str
334 :param address: The IP address of the gateway or host that
335 responded to the request.
336
337 :type min_rtt: float
338 :param min_rtt: The minimum round-trip time.
339
340 :type avg_rtt: float
341 :param avg_rtt: The average round-trip time.
342
343 :type max_rtt: float
344 :param max_rtt: The maximum round-trip time.
312 :param address: The IP address of the host that responded to the
313 request.
345314
346315 :type packets_sent: int
347316 :param packets_sent: The number of packets transmitted to the
348317 destination host.
349318
350 :type packets_received: int
351 :param packets_received: The number of packets sent by the remote
352 host and received by the current host.
353
354 '''
355 def __init__(self, address, min_rtt, avg_rtt, max_rtt,
356 packets_sent, packets_received):
357
319 :type rtts: list[float]
320 :param rtts: The list of round-trip times expressed in milliseconds.
321
322 '''
323 __slots__ = '_address', '_packets_sent', '_rtts'
324
325 def __init__(self, address, packets_sent, rtts):
358326 self._address = address
359 self._min_rtt = round(min_rtt, 3)
360 self._avg_rtt = round(avg_rtt, 3)
361 self._max_rtt = round(max_rtt, 3)
362327 self._packets_sent = packets_sent
363 self._packets_received = packets_received
328 self._rtts = rtts
364329
365330 def __repr__(self):
366331 return f'<Host [{self._address}]>'
367332
333 def __str__(self):
334 return f' {self._address}\n' + '-' * 60 + '\n' \
335 f' Packets sent: {self._packets_sent}\n' \
336 f' Packets received: {self.packets_received}\n' \
337 f' Packet loss: {self.packet_loss * 100}%\n' \
338 f' Round-trip times: {self.min_rtt} ms / ' \
339 f'{self.avg_rtt} ms / {self.max_rtt} ms\n' \
340 f' Jitter: {self.jitter} ms\n' + '-' * 60
341
368342 @property
369343 def address(self):
370344 '''
371 The IP address of the gateway or host that responded to the
372 request.
345 The IP address of the host that responded to the request.
373346
374347 '''
375348 return self._address
377350 @property
378351 def min_rtt(self):
379352 '''
380 The minimum round-trip time.
381
382 '''
383 return self._min_rtt
353 The minimum round-trip time in milliseconds.
354
355 '''
356 if not self._rtts:
357 return 0.0
358
359 return round(min(self._rtts), 3)
384360
385361 @property
386362 def avg_rtt(self):
387363 '''
388 The average round-trip time.
389
390 '''
391 return self._avg_rtt
364 The average round-trip time in milliseconds.
365
366 '''
367 if not self._rtts:
368 return 0.0
369
370 return round(sum(self._rtts) / len(self._rtts), 3)
392371
393372 @property
394373 def max_rtt(self):
395374 '''
396 The maximum round-trip time.
397
398 '''
399 return self._max_rtt
375 The maximum round-trip time in milliseconds.
376
377 '''
378 if not self._rtts:
379 return 0.0
380
381 return round(max(self._rtts), 3)
382
383 @property
384 def rtts(self):
385 '''
386 The list of round-trip times expressed in milliseconds.
387
388 '''
389 return self._rtts
400390
401391 @property
402392 def packets_sent(self):
403393 '''
404 The number of packets transmitted to the destination host.
394 The number of requests transmitted to the remote host.
405395
406396 '''
407397 return self._packets_sent
408398
409399 @property
410 def transmitted_packets(self):
411 '''
412 Deprecated: use the `packets_sent` property instead.
413
414 '''
415 print('[icmplib] Deprecation Warning: The '
416 '`transmitted_packets` property will be removed from '
417 'icmplib 2.0. Use the `packets_sent` property instead.')
418
419 return self._packets_sent
420
421 @property
422400 def packets_received(self):
423401 '''
424 The number of packets sent by the remote host and received by
425 the current host.
426
427 '''
428 return self._packets_received
429
430 @property
431 def received_packets(self):
432 '''
433 Deprecated: use the `packets_received` property instead.
434
435 '''
436 print('[icmplib] Deprecation Warning: The `received_packets` '
437 'property will be removed from icmplib 2.0. Use the '
438 '`packets_received` property instead.')
439
440 return self._packets_received
402 The number of ICMP responses received from the remote host.
403
404 '''
405 return len(self._rtts)
441406
442407 @property
443408 def packet_loss(self):
449414 if not self._packets_sent:
450415 return 0.0
451416
452 return 1 - self._packets_received / self._packets_sent
417 return round(1 - len(self._rtts) / self._packets_sent, 2)
418
419 @property
420 def jitter(self):
421 '''
422 The jitter in milliseconds, defined as the variance of the
423 latency of packets flowing through the network.
424
425 At least two ICMP responses are required to calculate the
426 jitter.
427
428 '''
429 sum_deltas = 0.0
430 num_deltas = len(self._rtts) - 1
431
432 if num_deltas < 1:
433 return 0.0
434
435 for i in range(num_deltas):
436 sum_deltas += abs(self._rtts[i] - self._rtts[i + 1])
437
438 return round(sum_deltas / num_deltas, 3)
453439
454440 @property
455441 def is_alive(self):
456442 '''
457 Indicate whether the host is reachable. Return a `boolean`.
458
459 '''
460 return self._packets_received > 0
443 Indicate whether the host is reachable.
444 Return a `boolean`.
445
446 '''
447 return len(self._rtts) > 0
461448
462449
463450 class Hop(Host):
466453 some features for the `traceroute` function.
467454
468455 :type address: str
469 :param address: The IP address of the gateway or host that
470 responded to the request.
471
472 :type min_rtt: float
473 :param min_rtt: The minimum round-trip time.
474
475 :type avg_rtt: float
476 :param avg_rtt: The average round-trip time.
477
478 :type max_rtt: float
479 :param max_rtt: The maximum round-trip time.
456 :param address: The IP address of the gateway or host that responded
457 to the request.
480458
481459 :type packets_sent: int
482460 :param packets_sent: The number of packets transmitted to the
483461 destination host.
484462
485 :type packets_received: int
486 :param packets_received: The number of packets sent by the remote
487 host and received by the current host.
463 :type rtts: list[float]
464 :param rtts: The list of round-trip times expressed in milliseconds.
488465
489466 :type distance: int
490 :param distance: The distance (in terms of hops) that separates the
467 :param distance: The distance, in terms of hops, that separates the
491468 remote host from the current machine.
492469
493470 '''
494 def __init__(self, address, min_rtt, avg_rtt, max_rtt,
495 packets_sent, packets_received, distance):
496
497 super().__init__(address, min_rtt, avg_rtt, max_rtt,
498 packets_sent, packets_received)
499
471 __slots__ = '_address', '_packets_sent', '_rtts', '_distance'
472
473 def __init__(self, address, packets_sent, rtts, distance):
474 super().__init__(address, packets_sent, rtts)
500475 self._distance = distance
501476
502477 def __repr__(self):
503478 return f'<Hop {self._distance} [{self._address}]>'
504479
480 def __str__(self):
481 return f' #{self._distance:<2} {super().__str__()[2:]}'
482
505483 @property
506484 def distance(self):
507485 '''
508 The distance (in terms of hops) that separates the remote host
486 The distance, in terms of hops, that separates the remote host
509487 from the current machine.
510488
511489 '''
0 '''
1 icmplib
2 ~~~~~~~
3
4 The power to forge ICMP packets and do ping and traceroute.
5
6 https://github.com/ValentinBELYN/icmplib
7
8 :copyright: Copyright 2017-2021 Valentin BELYN.
9 :license: GNU LGPLv3, see the LICENSE for details.
10
11 ~~~~~~~
12
13 This program is free software: you can redistribute it and/or
14 modify it under the terms of the GNU Lesser General Public License
15 as published by the Free Software Foundation, either version 3 of
16 the License, or (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU Lesser General Public License for more details.
22
23 You should have received a copy of the GNU Lesser General Public
24 License along with this program. If not, see
25 <https://www.gnu.org/licenses/>.
26 '''
27
28 import asyncio
29
30 from .ping import async_ping
31
32
33 async def async_multiping(addresses, count=2, interval=0.5, timeout=2,
34 concurrent_tasks=50, source=None, family=None, privileged=True,
35 **kwargs):
36 '''
37 Send ICMP Echo Request packets to several network hosts.
38
39 This function is non-blocking.
40
41 :type addresses: list[str]
42 :param addresses: The IP addresses of the hosts to which messages
43 should be sent. Hostnames and FQDNs are allowed but not
44 recommended. You can easily retrieve their IP address by calling
45 the built-in `async_resolve` function.
46
47 :type count: int, optional
48 :param count: The number of ping to perform per address.
49 Default to 2.
50
51 :type interval: int or float, optional
52 :param interval: The interval in seconds between sending each packet.
53 Default to 0.5.
54
55 :type timeout: int or float, optional
56 :param timeout: The maximum waiting time for receiving a reply in
57 seconds. Default to 2.
58
59 :type concurrent_tasks: int, optional
60 :param concurrent_tasks: The maximum number of concurrent tasks to
61 speed up processing. This value cannot exceed the maximum number
62 of file descriptors configured on the operating system.
63 Default to 50.
64
65 :type source: str, optional
66 :param source: The IP address from which you want to send packets.
67 By default, the interface is automatically chosen according to
68 the specified destinations. This parameter should not be used if
69 you are passing both IPv4 and IPv6 addresses to this function.
70
71 :type family: int, optional
72 :param family: The address family if a hostname or FQDN is specified.
73 Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default,
74 this function searches for IPv4 addresses first before searching
75 for IPv6 addresses.
76
77 :type privileged: bool, optional
78 :param privileged: When this option is enabled, this library fully
79 manages the exchanges and the structure of ICMP packets.
80 Disable this option if you want to use this function without
81 root privileges and let the kernel handle ICMP headers.
82 Default to True.
83 Only available on Unix systems. Ignored on Windows.
84
85 Advanced (**kwags):
86
87 :type payload: bytes, optional
88 :param payload: The payload content in bytes. A random payload is
89 used by default.
90
91 :type payload_size: int, optional
92 :param payload_size: The payload size. Ignored when the `payload`
93 parameter is set. Default to 56.
94
95 :type traffic_class: int, optional
96 :param traffic_class: The traffic class of ICMP packets.
97 Provides a defined level of service to packets by setting the DS
98 Field (formerly TOS) or the Traffic Class field of IP headers.
99 Packets are delivered with the minimum priority by default
100 (Best-effort delivery).
101 Intermediate routers must be able to support this feature.
102 Only available on Unix systems. Ignored on Windows.
103
104 :rtype: list[Host]
105 :returns: A list of `Host` objects containing statistics about the
106 desired destinations. The list is sorted in the same order as
107 the addresses passed in parameters.
108
109 :raises NameLookupError: If you pass a hostname or FQDN in
110 parameters and it does not exist or cannot be resolved.
111 :raises SocketPermissionError: If the privileges are insufficient to
112 create the socket.
113 :raises SocketAddressError: If the source address cannot be assigned
114 to the socket.
115 :raises ICMPSocketError: If another error occurs. See the
116 `ICMPv4Socket` or `ICMPv6Socket` class for details.
117
118 Usage::
119
120 >>> import asyncio
121 >>> from icmplib import async_multiping
122 >>> hosts = asyncio.run(async_multiping(['10.0.0.5', '::1']))
123
124 >>> for host in hosts:
125 ... if host.is_alive:
126 ... print(f'{host.address} is up!')
127 ... else:
128 ... print(f'{host.address} is down!')
129
130 10.0.0.5 is down!
131 ::1 is up!
132
133 See the `Host` class for details.
134
135 '''
136 loop = asyncio.get_running_loop()
137 tasks = []
138 tasks_pending = set()
139
140 for address in addresses:
141 if len(tasks_pending) >= concurrent_tasks:
142 _, tasks_pending = await asyncio.wait(
143 tasks_pending,
144 return_when=asyncio.FIRST_COMPLETED)
145
146 task = loop.create_task(
147 async_ping(
148 address=address,
149 count=count,
150 interval=interval,
151 timeout=timeout,
152 source=source,
153 family=family,
154 privileged=privileged,
155 **kwargs))
156
157 tasks.append(task)
158 tasks_pending.add(task)
159
160 await asyncio.wait(tasks_pending)
161
162 return [task.result() for task in tasks]
163
164
165 def multiping(addresses, count=2, interval=0.5, timeout=2,
166 concurrent_tasks=50, source=None, family=None, privileged=True,
167 **kwargs):
168 '''
169 Send ICMP Echo Request packets to several network hosts.
170
171 :type addresses: list[str]
172 :param addresses: The IP addresses of the hosts to which messages
173 should be sent. Hostnames and FQDNs are allowed but not
174 recommended. You can easily retrieve their IP address by calling
175 the built-in `resolve` function.
176
177 :type count: int, optional
178 :param count: The number of ping to perform per address.
179 Default to 2.
180
181 :type interval: int or float, optional
182 :param interval: The interval in seconds between sending each packet.
183 Default to 0.5.
184
185 :type timeout: int or float, optional
186 :param timeout: The maximum waiting time for receiving a reply in
187 seconds. Default to 2.
188
189 :type concurrent_tasks: int, optional
190 :param concurrent_tasks: The maximum number of concurrent tasks to
191 speed up processing. This value cannot exceed the maximum number
192 of file descriptors configured on the operating system.
193 Default to 50.
194
195 :type source: str, optional
196 :param source: The IP address from which you want to send packets.
197 By default, the interface is automatically chosen according to
198 the specified destinations. This parameter should not be used if
199 you are passing both IPv4 and IPv6 addresses to this function.
200
201 :type family: int, optional
202 :param family: The address family if a hostname or FQDN is specified.
203 Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default,
204 this function searches for IPv4 addresses first before searching
205 for IPv6 addresses.
206
207 :type privileged: bool, optional
208 :param privileged: When this option is enabled, this library fully
209 manages the exchanges and the structure of ICMP packets.
210 Disable this option if you want to use this function without
211 root privileges and let the kernel handle ICMP headers.
212 Default to True.
213 Only available on Unix systems. Ignored on Windows.
214
215 Advanced (**kwags):
216
217 :type payload: bytes, optional
218 :param payload: The payload content in bytes. A random payload is
219 used by default.
220
221 :type payload_size: int, optional
222 :param payload_size: The payload size. Ignored when the `payload`
223 parameter is set. Default to 56.
224
225 :type traffic_class: int, optional
226 :param traffic_class: The traffic class of ICMP packets.
227 Provides a defined level of service to packets by setting the DS
228 Field (formerly TOS) or the Traffic Class field of IP headers.
229 Packets are delivered with the minimum priority by default
230 (Best-effort delivery).
231 Intermediate routers must be able to support this feature.
232 Only available on Unix systems. Ignored on Windows.
233
234 :rtype: list[Host]
235 :returns: A list of `Host` objects containing statistics about the
236 desired destinations. The list is sorted in the same order as
237 the addresses passed in parameters.
238
239 :raises NameLookupError: If you pass a hostname or FQDN in
240 parameters and it does not exist or cannot be resolved.
241 :raises SocketPermissionError: If the privileges are insufficient to
242 create the socket.
243 :raises SocketAddressError: If the source address cannot be assigned
244 to the socket.
245 :raises ICMPSocketError: If another error occurs. See the
246 `ICMPv4Socket` or `ICMPv6Socket` class for details.
247
248 Usage::
249
250 >>> from icmplib import multiping
251 >>> hosts = multiping(['10.0.0.5', '127.0.0.1', '::1'])
252
253 >>> for host in hosts:
254 ... if host.is_alive:
255 ... print(f'{host.address} is up!')
256 ... else:
257 ... print(f'{host.address} is down!')
258
259 10.0.0.5 is down!
260 127.0.0.1 is up!
261 ::1 is up!
262
263 See the `Host` class for details.
264
265 '''
266 return asyncio.run(
267 async_multiping(
268 addresses=addresses,
269 count=count,
270 interval=interval,
271 timeout=timeout,
272 concurrent_tasks=concurrent_tasks,
273 source=source,
274 family=family,
275 privileged=privileged,
276 **kwargs))
11 icmplib
22 ~~~~~~~
33
4 The power to forge ICMP packets and do ping and traceroute.
5
46 https://github.com/ValentinBELYN/icmplib
57
6 :copyright: Copyright 2017-2020 Valentin BELYN.
8 :copyright: Copyright 2017-2021 Valentin BELYN.
79 :license: GNU LGPLv3, see the LICENSE for details.
810
911 ~~~~~~~
2325 <https://www.gnu.org/licenses/>.
2426 '''
2527
26 from threading import Thread
28 import asyncio
2729 from time import sleep
2830
29 from .sockets import ICMPv4Socket, ICMPv6Socket
31 from .sockets import ICMPv4Socket, ICMPv6Socket, AsyncSocket
3032 from .models import ICMPRequest, Host
31 from .exceptions import *
32 from .utils import PID, resolve, is_ipv6_address
33
34
35 class PingThread(Thread):
36 def __init__(self, **kwargs):
37 super().__init__()
38 self._kwargs = kwargs
39 self._host = None
40
41 def run(self):
42 self._host = ping(**self._kwargs)
43
44 @property
45 def host(self):
46 return self._host
47
48
49 def ping(address, count=4, interval=1, timeout=2, id=PID, **kwargs):
50 '''
51 Send ICMP ECHO_REQUEST packets to a network host.
33 from .exceptions import ICMPLibError
34 from .utils import *
35
36
37 def ping(address, count=4, interval=1, timeout=2, id=None, source=None,
38 family=None, privileged=True, **kwargs):
39 '''
40 Send ICMP Echo Request packets to a network host.
5241
5342 :type address: str
54 :param address: The IP address of the gateway or host to which
55 the message should be sent.
56
57 :type count: int
58 :param count: (Optional) The number of ping to perform.
59
60 :type interval: int or float
61 :param interval: (Optional) The interval in seconds between sending
62 each packet.
63
64 :type timeout: int or float
65 :param timeout: (Optional) The maximum waiting time for receiving
66 a reply in seconds.
67
68 :type id: int
69 :param id: (Optional) The identifier of the request. Used to match
70 the reply with the request. In practice, a unique identifier is
71 used for every ping process.
72
73 :param **kwargs: (Optional) Advanced use: arguments passed to
74 `ICMPRequest` objects.
43 :param address: The IP address, hostname or FQDN of the host to
44 which messages should be sent. For deterministic behavior,
45 prefer to use an IP address.
46
47 :type count: int, optional
48 :param count: The number of ping to perform. Default to 4.
49
50 :type interval: int or float, optional
51 :param interval: The interval in seconds between sending each packet.
52 Default to 1.
53
54 :type timeout: int or float, optional
55 :param timeout: The maximum waiting time for receiving a reply in
56 seconds. Default to 2.
57
58 :type id: int, optional
59 :param id: The identifier of ICMP requests. Used to match the
60 responses with requests. In practice, a unique identifier should
61 be used for every ping process. On Linux, this identifier is
62 ignored when the `privileged` parameter is disabled. The library
63 handles this identifier itself by default.
64
65 :type source: str, optional
66 :param source: The IP address from which you want to send packets.
67 By default, the interface is automatically chosen according to
68 the specified destination.
69
70 :type family: int, optional
71 :param family: The address family if a hostname or FQDN is specified.
72 Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default,
73 this function searches for IPv4 addresses first before searching
74 for IPv6 addresses.
75
76 :type privileged: bool, optional
77 :param privileged: When this option is enabled, this library fully
78 manages the exchanges and the structure of ICMP packets.
79 Disable this option if you want to use this function without
80 root privileges and let the kernel handle ICMP headers.
81 Default to True.
82 Only available on Unix systems. Ignored on Windows.
83
84 Advanced (**kwags):
85
86 :type payload: bytes, optional
87 :param payload: The payload content in bytes. A random payload is
88 used by default.
89
90 :type payload_size: int, optional
91 :param payload_size: The payload size. Ignored when the `payload`
92 parameter is set. Default to 56.
93
94 :type traffic_class: int, optional
95 :param traffic_class: The traffic class of ICMP packets.
96 Provides a defined level of service to packets by setting the DS
97 Field (formerly TOS) or the Traffic Class field of IP headers.
98 Packets are delivered with the minimum priority by default
99 (Best-effort delivery).
100 Intermediate routers must be able to support this feature.
101 Only available on Unix systems. Ignored on Windows.
75102
76103 :rtype: Host
77104 :returns: A `Host` object containing statistics about the desired
78105 destination.
79106
80 :raises SocketPermissionError: If the permissions are insufficient
81 to create a socket.
107 :raises NameLookupError: If you pass a hostname or FQDN in
108 parameters and it does not exist or cannot be resolved.
109 :raises SocketPermissionError: If the privileges are insufficient to
110 create the socket.
111 :raises SocketAddressError: If the source address cannot be assigned
112 to the socket.
113 :raises ICMPSocketError: If another error occurs. See the
114 `ICMPv4Socket` or `ICMPv6Socket` class for details.
82115
83116 Usage::
84117
85118 >>> from icmplib import ping
86119 >>> host = ping('1.1.1.1')
87
88120 >>> host.avg_rtt
89121 13.2
90
91122 >>> host.is_alive
92123 True
93124
94125 See the `Host` class for details.
95126
96127 '''
97 address = resolve(address)
128 if is_hostname(address):
129 address = resolve(address, family)[0]
98130
99131 if is_ipv6_address(address):
100 socket = ICMPv6Socket()
101
132 _Socket = ICMPv6Socket
102133 else:
103 socket = ICMPv4Socket()
104
134 _Socket = ICMPv4Socket
135
136 id = id or unique_identifier()
105137 packets_sent = 0
106 packets_received = 0
107
108 min_rtt = float('inf')
109 avg_rtt = 0.0
110 max_rtt = 0.0
111
112 for sequence in range(count):
113 request = ICMPRequest(
114 destination=address,
115 id=id,
116 sequence=sequence,
117 timeout=timeout,
118 **kwargs)
119
120 try:
121 socket.send(request)
122 packets_sent += 1
123
124 reply = socket.receive()
125 reply.raise_for_status()
126 packets_received += 1
127
128 round_trip_time = (reply.time - request.time) * 1000
129 avg_rtt += round_trip_time
130 min_rtt = min(round_trip_time, min_rtt)
131 max_rtt = max(round_trip_time, max_rtt)
132
133 if sequence < count - 1:
138 rtts = []
139
140 with _Socket(source, privileged) as sock:
141 for sequence in range(count):
142 if sequence > 0:
134143 sleep(interval)
135144
136 except ICMPLibError:
137 pass
138
139 if packets_received:
140 avg_rtt /= packets_received
141
145 request = ICMPRequest(
146 destination=address,
147 id=id,
148 sequence=sequence,
149 **kwargs)
150
151 try:
152 sock.send(request)
153 packets_sent += 1
154
155 reply = sock.receive(request, timeout)
156 reply.raise_for_status()
157
158 rtt = (reply.time - request.time) * 1000
159 rtts.append(rtt)
160
161 except ICMPLibError:
162 pass
163
164 return Host(address, packets_sent, rtts)
165
166
167 async def async_ping(address, count=4, interval=1, timeout=2, id=None,
168 source=None, family=None, privileged=True, **kwargs):
169 '''
170 Send ICMP Echo Request packets to a network host.
171
172 This function is non-blocking.
173
174 :type address: str
175 :param address: The IP address, hostname or FQDN of the host to
176 which messages should be sent. For deterministic behavior,
177 prefer to use an IP address.
178
179 :type count: int, optional
180 :param count: The number of ping to perform. Default to 4.
181
182 :type interval: int or float, optional
183 :param interval: The interval in seconds between sending each packet.
184 Default to 1.
185
186 :type timeout: int or float, optional
187 :param timeout: The maximum waiting time for receiving a reply in
188 seconds. Default to 2.
189
190 :type id: int, optional
191 :param id: The identifier of ICMP requests. Used to match the
192 responses with requests. In practice, a unique identifier should
193 be used for every ping process. On Linux, this identifier is
194 ignored when the `privileged` parameter is disabled. The library
195 handles this identifier itself by default.
196
197 :type source: str, optional
198 :param source: The IP address from which you want to send packets.
199 By default, the interface is automatically chosen according to
200 the specified destination.
201
202 :type family: int, optional
203 :param family: The address family if a hostname or FQDN is specified.
204 Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default,
205 this function searches for IPv4 addresses first before searching
206 for IPv6 addresses.
207
208 :type privileged: bool, optional
209 :param privileged: When this option is enabled, this library fully
210 manages the exchanges and the structure of ICMP packets.
211 Disable this option if you want to use this function without
212 root privileges and let the kernel handle ICMP headers.
213 Default to True.
214 Only available on Unix systems. Ignored on Windows.
215
216 Advanced (**kwags):
217
218 :type payload: bytes, optional
219 :param payload: The payload content in bytes. A random payload is
220 used by default.
221
222 :type payload_size: int, optional
223 :param payload_size: The payload size. Ignored when the `payload`
224 parameter is set. Default to 56.
225
226 :type traffic_class: int, optional
227 :param traffic_class: The traffic class of ICMP packets.
228 Provides a defined level of service to packets by setting the DS
229 Field (formerly TOS) or the Traffic Class field of IP headers.
230 Packets are delivered with the minimum priority by default
231 (Best-effort delivery).
232 Intermediate routers must be able to support this feature.
233 Only available on Unix systems. Ignored on Windows.
234
235 :rtype: Host
236 :returns: A `Host` object containing statistics about the desired
237 destination.
238
239 :raises NameLookupError: If you pass a hostname or FQDN in
240 parameters and it does not exist or cannot be resolved.
241 :raises SocketPermissionError: If the privileges are insufficient to
242 create the socket.
243 :raises SocketAddressError: If the source address cannot be assigned
244 to the socket.
245 :raises ICMPSocketError: If another error occurs. See the
246 `ICMPv4Socket` or `ICMPv6Socket` class for details.
247
248 Usage::
249
250 >>> import asyncio
251 >>> from icmplib import async_ping
252 >>> host = asyncio.run(async_ping('1.1.1.1'))
253 >>> host.avg_rtt
254 13.2
255 >>> host.is_alive
256 True
257
258 See the `Host` class for details.
259
260 '''
261 if is_hostname(address):
262 address = (await async_resolve(address, family))[0]
263
264 if is_ipv6_address(address):
265 _Socket = ICMPv6Socket
142266 else:
143 min_rtt = 0.0
144
145 host = Host(
146 address=address,
147 min_rtt=min_rtt,
148 avg_rtt=avg_rtt,
149 max_rtt=max_rtt,
150 packets_sent=packets_sent,
151 packets_received=packets_received)
152
153 socket.close()
154
155 return host
156
157
158 def multiping(addresses, count=2, interval=1, timeout=2, id=PID,
159 max_threads=10, **kwargs):
160 '''
161 Send ICMP ECHO_REQUEST packets to multiple network hosts.
162
163 :type addresses: list of str
164 :param addresses: The IP addresses of the gateways or hosts to
165 which messages should be sent.
166
167 :type count: int
168 :param count: (Optional) The number of ping to perform per address.
169
170 :type interval: int or float
171 :param interval: (Optional) The interval in seconds between sending
172 each packet.
173
174 :type timeout: int or float
175 :param timeout: (Optional) The maximum waiting time for receiving
176 a reply in seconds.
177
178 :type id: int
179 :param id: (Optional) The identifier of the requests. This
180 identifier will be incremented by one for each destination.
181
182 :type max_threads: int
183 :param max_threads: (Optional) The number of threads allowed to
184 speed up processing.
185
186 :param **kwargs: (Optional) Advanced use: arguments passed to
187 `ICMPRequest` objects.
188
189 :rtype: list of Host
190 :returns: A list of `Host` objects containing statistics about the
191 desired destinations. The list is sorted in the same order as
192 the addresses passed in parameters.
193
194 :raises SocketPermissionError: If the permissions are insufficient
195 to create a socket.
196
197 Usage::
198
199 >>> from icmplib import multiping
200 >>> hosts = multiping(['10.0.0.5', '127.0.0.1', '::1'])
201
202 >>> for host in hosts:
203 ... if host.is_alive:
204 ... print(f'{host.address} is alive!')
205 ...
206 ... else:
207 ... print(f'{host.address} is dead!')
208 ...
209 10.0.0.5 is dead!
210 127.0.0.1 is alive!
211 ::1 is alive!
212
213 See the `Host` class for details.
214
215 '''
216 hosts = []
217 inactive_threads = []
218 active_threads = []
219
220 for i, address in enumerate(addresses):
221 thread = PingThread(
222 address=address,
223 count=count,
224 interval=interval,
225 timeout=timeout,
226 id=id + i,
227 **kwargs)
228
229 inactive_threads.append(thread)
230
231 while inactive_threads:
232 thread = inactive_threads.pop(0)
233 thread.start()
234 active_threads.append(thread)
235
236 if (inactive_threads and
237 len(active_threads) < max_threads):
238 sleep(0.05)
239 continue
240
241 while active_threads:
242 thread = active_threads.pop(0)
243 thread.join()
244 hosts.append(thread.host)
245
246 return hosts
267 _Socket = ICMPv4Socket
268
269 id = id or unique_identifier()
270 packets_sent = 0
271 rtts = []
272
273 with AsyncSocket(_Socket(source, privileged)) as sock:
274 for sequence in range(count):
275 if sequence > 0:
276 await asyncio.sleep(interval)
277
278 request = ICMPRequest(
279 destination=address,
280 id=id,
281 sequence=sequence,
282 **kwargs)
283
284 try:
285 sock.send(request)
286 packets_sent += 1
287
288 reply = await sock.receive(request, timeout)
289 reply.raise_for_status()
290
291 rtt = (reply.time - request.time) * 1000
292 rtts.append(rtt)
293
294 except ICMPLibError:
295 pass
296
297 return Host(address, packets_sent, rtts)
11 icmplib
22 ~~~~~~~
33
4 The power to forge ICMP packets and do ping and traceroute.
5
46 https://github.com/ValentinBELYN/icmplib
57
6 :copyright: Copyright 2017-2020 Valentin BELYN.
8 :copyright: Copyright 2017-2021 Valentin BELYN.
79 :license: GNU LGPLv3, see the LICENSE for details.
810
911 ~~~~~~~
2325 <https://www.gnu.org/licenses/>.
2426 '''
2527
26 import socket
28 import socket, asyncio
2729 from struct import pack, unpack
2830 from time import time
2931
30 from .ip import IPv4Socket, IPv6Socket
31 from .models import ICMPRequest, ICMPReply
32 from .models import ICMPReply
3233 from .exceptions import *
33 from .utils import random_byte_message
34
35
36 # Echo Request and Echo Reply messages -- RFC 792 / 4443
37 #
38 # 0 1 2 3
39 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
40 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41 # | Type | Code | Checksum |
42 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43 # | Identifier | Sequence Number |
44 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45 # | Data ...
46 # +-+-+-+-+-
47 #
48 # ICMPv4 Error message -- RFC 792
49 #
50 # 0 1 2 3
51 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
52 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
53 # | Type | Code | Checksum |
54 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55 # | Unused / Depends on the error |
56 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
57 # | Internet Header + 64 bits of Original Data Datagram |
58 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
59 #
60 # ICMPv6 Error message -- RFC 4443
61 #
62 # 0 1 2 3
63 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
64 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65 # | Type | Code | Checksum |
66 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
67 # | Unused / Depends on the error |
68 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
69 # | Original packet without exceed the minimum IPv6 MTU |
70 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
71
72
73 class ICMPConfig:
74 IP_PROTOCOL = -1
75 IP_SOCKET = None
76
77 ICMP_HEADER_OFFSET = -1
78 ICMP_CODE_OFFSET = ICMP_HEADER_OFFSET + 1
79 ICMP_CHECKSUM_OFFSET = ICMP_HEADER_OFFSET + 2
80 ICMP_ID_OFFSET = ICMP_HEADER_OFFSET + 4
81 ICMP_SEQUENCE_OFFSET = ICMP_HEADER_OFFSET + 6
82 ICMP_PAYLOAD_OFFSET = ICMP_HEADER_OFFSET + 8
83
84 ICMP_ECHO_REQUEST = -1
85 ICMP_ECHO_REPLY = -1
86
87
88 class ICMPv4Config(ICMPConfig):
89 IP_PROTOCOL = 1
90 IP_SOCKET = IPv4Socket
91
92 ICMP_HEADER_OFFSET = 20
93 ICMP_CODE_OFFSET = ICMP_HEADER_OFFSET + 1
94 ICMP_CHECKSUM_OFFSET = ICMP_HEADER_OFFSET + 2
95 ICMP_ID_OFFSET = ICMP_HEADER_OFFSET + 4
96 ICMP_SEQUENCE_OFFSET = ICMP_HEADER_OFFSET + 6
97 ICMP_PAYLOAD_OFFSET = ICMP_HEADER_OFFSET + 8
98
99 ICMP_ECHO_REQUEST = 8
100 ICMP_ECHO_REPLY = 0
101
102
103 class ICMPv6Config(ICMPConfig):
104 IP_PROTOCOL = 58
105 IP_SOCKET = IPv6Socket
106
107 ICMP_HEADER_OFFSET = 0
108 ICMP_CODE_OFFSET = ICMP_HEADER_OFFSET + 1
109 ICMP_CHECKSUM_OFFSET = ICMP_HEADER_OFFSET + 2
110 ICMP_ID_OFFSET = ICMP_HEADER_OFFSET + 4
111 ICMP_SEQUENCE_OFFSET = ICMP_HEADER_OFFSET + 6
112 ICMP_PAYLOAD_OFFSET = ICMP_HEADER_OFFSET + 8
113
114 ICMP_ECHO_REQUEST = 128
115 ICMP_ECHO_REPLY = 129
34 from .utils import PLATFORM_LINUX, PLATFORM_MACOS, PLATFORM_WINDOWS
11635
11736
11837 class ICMPSocket:
11938 '''
12039 Base class for ICMP sockets.
12140
122 :type config: ICMPConfig
123 :param config: The ICMP socket configuration used to create and
124 read ICMP packets.
125
126 :raises SocketPermissionError: If the permissions are insufficient
127 to create the socket.
41 :type address: str, optional
42 :param address: The IP address from which you want to listen and
43 send packets. By default, the socket listens on all interfaces.
44
45 :type privileged: bool, optional
46 :param privileged: When this option is enabled, the socket fully
47 manages the exchanges and the structure of the ICMP packets.
48 Disable this option if you want to instantiate and use the
49 socket without root privileges and let the kernel handle ICMP
50 headers. Default to True.
51 Only available on Unix systems. Ignored on Windows.
52
53 :raises SocketPermissionError: If the privileges are insufficient to
54 create the socket.
55 :raises SocketAddressError: If the requested address cannot be
56 assigned to the socket.
57 :raises ICMPSocketError: If another error occurs while creating the
58 socket.
12859
12960 '''
130 def __init__(self, config):
131 self._socket = None
132 self._config = config
133 self._last_request = None
61 __slots__ = '_sock', '_address', '_privileged'
62
63 _IP_VERSION = -1
64 _ICMP_HEADER_OFFSET = -1
65 _ICMP_HEADER_REAL_OFFSET = -1
66
67 _ICMP_CODE_OFFSET = _ICMP_HEADER_OFFSET + 1
68 _ICMP_CHECKSUM_OFFSET = _ICMP_HEADER_OFFSET + 2
69 _ICMP_ID_OFFSET = _ICMP_HEADER_OFFSET + 4
70 _ICMP_SEQUENCE_OFFSET = _ICMP_HEADER_OFFSET + 6
71 _ICMP_PAYLOAD_OFFSET = _ICMP_HEADER_OFFSET + 8
72
73 _ICMP_ECHO_REQUEST = -1
74 _ICMP_ECHO_REPLY = -1
75
76 def __init__(self, address=None, privileged=True):
77 self._sock = None
78 self._address = address
79
80 # The Linux kernel allows unprivileged users to use datagram
81 # sockets (SOCK_DGRAM) to send ICMP requests. This feature is
82 # now supported by the majority of Unix systems.
83 # Windows is not compatible.
84 self._privileged = privileged or PLATFORM_WINDOWS
13485
13586 try:
136 self._socket = config.IP_SOCKET(
137 config.IP_PROTOCOL)
138
139 except OSError:
140 raise SocketPermissionError
87 self._sock = self._create_socket(
88 socket.SOCK_RAW if self._privileged else
89 socket.SOCK_DGRAM)
90
91 if address:
92 self._sock.bind((address, 0))
93
94 except OSError as err:
95 if err.errno in (1, 13, 10013):
96 raise SocketPermissionError(privileged)
97
98 if err.errno in (-9, 49, 99, 10049, 11001):
99 raise SocketAddressError(address)
100
101 raise ICMPSocketError(str(err))
102
103 def __enter__(self):
104 '''
105 Return this object.
106
107 '''
108 return self
109
110 def __exit__(self, type, value, traceback):
111 '''
112 Call the `close` method.
113
114 '''
115 self.close()
141116
142117 def __del__(self):
143118 '''
146121 '''
147122 self.close()
148123
149 def _create_header(self, type, code, checksum, id, sequence):
150 '''
151 Create the ICMP header of a packet.
152
153 '''
154 # B: 8 bits
155 # H: 16 bits
156 return pack('!2B3H', type, code, checksum, id, sequence)
124 def _create_socket(self, type):
125 '''
126 Create and return a new socket. Must be overridden.
127
128 '''
129 raise NotImplementedError
130
131 def _set_ttl(self, ttl):
132 '''
133 Set the time to live of every IP packet originating from this
134 socket. Must be overridden.
135
136 '''
137 raise NotImplementedError
138
139 def _set_traffic_class(self, traffic_class):
140 '''
141 Set the DS Field (formerly TOS) or the Traffic Class field of
142 every IP packet originating from this socket. Must be
143 overridden.
144
145 '''
146 raise NotImplementedError
157147
158148 def _checksum(self, data):
159149 '''
160 Calculate the checksum of a packet.
161
162 '''
150 Compute the checksum of an ICMP packet. Checksums are used to
151 verify the integrity of packets.
152
153 '''
154 sum = 0
163155 data += b'\x00'
164 end = len(data) - 1
165 sum = 0
166
167 for i in range(0, end, 2):
156
157 for i in range(0, len(data) - 1, 2):
168158 sum += (data[i] << 8) + data[i + 1]
169 sum = (sum >> 16) + (sum & 0xffff)
159 sum = (sum & 0xffff) + (sum >> 16)
170160
171161 sum = ~sum & 0xffff
172162
174164
175165 def _create_packet(self, id, sequence, payload):
176166 '''
177 Create a packet.
178
179 '''
180 type = self._config.ICMP_ECHO_REQUEST
181
182 # Temporary header to calculate the checksum
183 header = self._create_header(type, 0, 0, id, sequence)
167 Build an ICMP packet from an identifier, a sequence number and
168 a payload.
169
170 This method returns the newly created ICMP header concatenated
171 to the payload passed in parameters.
172
173 '''
174 checksum = 0
175
176 # Temporary ICMP header to compute the checksum
177 header = pack('!2B3H', self._ICMP_ECHO_REQUEST, 0, checksum,
178 id, sequence)
179
184180 checksum = self._checksum(header + payload)
185181
186 # Definitive header
187 header = self._create_header(type, 0, checksum, id, sequence)
182 # Definitive ICMP header
183 header = pack('!2B3H', self._ICMP_ECHO_REQUEST, 0, checksum,
184 id, sequence)
188185
189186 return header + payload
190187
191 def _read_reply(self, packet, source, reply_time):
192 '''
193 Read a reply from bytes. Return an `ICMPReply` object or `None`
194 if the reply cannot be parsed.
195
196 '''
197 bytes_received = (
198 len(packet)
199 - self._config.ICMP_HEADER_OFFSET)
200
201 if len(packet) < self._config.ICMP_CHECKSUM_OFFSET:
188 def _parse_reply(self, packet, source, current_time):
189 '''
190 Parse an ICMP reply from bytes.
191
192 This method returns an `ICMPReply` object or `None` if the reply
193 cannot be parsed.
194
195 '''
196 # On Linux, the IP header is missing when a datagram socket is
197 # used (SOCK_DGRAM). To keep the same behavior on all operating
198 # systems including macOS which has this header, we add a
199 # padding of the size of the missing IP header.
200 if not self._privileged and PLATFORM_LINUX:
201 packet = b'\x00' * self._ICMP_HEADER_OFFSET + packet
202
203 bytes_received = len(packet) - self._ICMP_HEADER_OFFSET
204
205 if len(packet) < self._ICMP_CHECKSUM_OFFSET:
202206 return None
203207
204208 type, code = unpack('!2B', packet[
205 self._config.ICMP_HEADER_OFFSET:
206 self._config.ICMP_CHECKSUM_OFFSET])
207
208 if type != self._config.ICMP_ECHO_REPLY:
209 packet = packet[self._config.ICMP_PAYLOAD_OFFSET:]
210
211 if len(packet) < self._config.ICMP_PAYLOAD_OFFSET:
209 self._ICMP_HEADER_OFFSET:
210 self._ICMP_CHECKSUM_OFFSET])
211
212 if type != self._ICMP_ECHO_REPLY:
213 packet = packet[
214 self._ICMP_PAYLOAD_OFFSET
215 - self._ICMP_HEADER_OFFSET
216 + self._ICMP_HEADER_REAL_OFFSET:]
217
218 if len(packet) < self._ICMP_PAYLOAD_OFFSET:
212219 return None
213220
214221 id, sequence = unpack('!2H', packet[
215 self._config.ICMP_ID_OFFSET:
216 self._config.ICMP_PAYLOAD_OFFSET
217 ])
218
219 reply = ICMPReply(
222 self._ICMP_ID_OFFSET:
223 self._ICMP_PAYLOAD_OFFSET])
224
225 return ICMPReply(
220226 source=source,
227 family=self._IP_VERSION,
221228 id=id,
222229 sequence=sequence,
223230 type=type,
224231 code=code,
225232 bytes_received=bytes_received,
226 time=reply_time)
227
228 return reply
233 time=current_time)
229234
230235 def send(self, request):
231236 '''
232 Send a request to a host.
237 Send an ICMP request message over the network to a remote host.
233238
234239 This operation is non-blocking. Use the `receive` method to get
235240 the reply.
236241
237242 :type request: ICMPRequest
238 :param request: The ICMP request you have created.
239
240 :raises SocketBroadcastError: If a broadcast address is used
241 and the corresponding option is not enabled on the socket
243 :param request: The ICMP request you have created. If the socket
244 is used in non-privileged mode on a Linux system, the
245 identifier defined in the request will be replaced by the
246 kernel.
247
248 :raises SocketBroadcastError: If a broadcast address is used and
249 the corresponding option is not enabled on the socket
242250 (ICMPv4 only).
243251 :raises SocketUnavailableError: If the socket is closed.
244252 :raises ICMPSocketError: If another error occurs while sending.
245253
246254 '''
247 if not self._socket:
255 if not self._sock:
248256 raise SocketUnavailableError
249257
250 payload = request.payload
251
252 if not payload:
253 payload = random_byte_message(
254 size=request.payload_size)
255
256 packet = self._create_packet(
257 id=request.id,
258 sequence=request.sequence,
259 payload=payload)
260
261 self._socket.ttl = request.ttl
262 self._socket.traffic_class = request.traffic_class
263
264 request._time = time()
265
266258 try:
267 # The packet is actually the Ethernet payload (without the
268 # IP header): the variable name will be changed in future
269 # versions
270 self._socket.send(
271 payload=packet,
272 address=request.destination,
273 port=0)
274
275 self._last_request = request
259 sock_destination = socket.getaddrinfo(
260 host=request.destination,
261 port=None,
262 family=self._sock.family,
263 type=self._sock.type)[0][4]
264
265 packet = self._create_packet(
266 id=request.id,
267 sequence=request.sequence,
268 payload=request.payload)
269
270 self._set_ttl(request.ttl)
271 self._set_traffic_class(request.traffic_class)
272
273 request._time = time()
274 self._sock.sendto(packet, sock_destination)
275
276 # On Linux, the ICMP request identifier is replaced by the
277 # kernel with a random port number when a datagram socket is
278 # used (SOCK_DGRAM). So, we update the request created by
279 # the user to take this new identifier into account.
280 if not self._privileged and PLATFORM_LINUX:
281 request._id = self._sock.getsockname()[1]
276282
277283 except PermissionError:
278284 raise SocketBroadcastError
280286 except OSError as err:
281287 raise ICMPSocketError(str(err))
282288
283 def receive(self):
284 '''
285 Receive a reply from a host.
289 def receive(self, request=None, timeout=2):
290 '''
291 Receive an ICMP reply message from the socket.
286292
287293 This method can be called multiple times if you expect several
288 responses (as with a broadcast address).
294 responses as with a broadcast address.
295
296 :type request: ICMPRequest, optional
297 :param request: The ICMP request to use to match the response.
298 By default, all ICMP packets arriving on the socket are
299 returned.
300
301 :type timeout: int or float, optional
302 :param timeout: The maximum waiting time for receiving the
303 response in seconds. Default to 2.
304
305 :rtype: ICMPReply
306 :returns: An `ICMPReply` object representing the response of the
307 desired destination or an upstream gateway. See the
308 `ICMPReply` class for details.
289309
290310 :raises TimeoutExceeded: If no response is received before the
291 timeout defined in the request.
292 This exception is also useful for stopping a possible loop
293 in case of multiple responses.
311 timeout specified in parameters.
294312 :raises SocketUnavailableError: If the socket is closed.
295 :raises ICMPSocketError: If another error occurs while
296 receiving.
297
298 :rtype: ICMPReply
299 :returns: An `ICMPReply` object containing the reply of the
300 desired destination.
301
302 See the `ICMPReply` class for details.
303
304 '''
305 if not self._socket:
313 :raises ICMPSocketError: If another error occurs while receiving.
314
315 '''
316 if not self._sock:
306317 raise SocketUnavailableError
307318
308 if not self._last_request:
309 raise TimeoutExceeded(0)
310
311 request = self._last_request
312
313 current_time = time()
314 self._socket.timeout = request.timeout
315 timeout = current_time + request.timeout
319 self._sock.settimeout(timeout)
320 time_limit = time() + timeout
316321
317322 try:
318323 while True:
319 packet, address, port = self._socket.receive()
320 reply_time = time()
321
322 if reply_time > timeout:
324 response = self._sock.recvfrom(1024)
325 current_time = time()
326
327 packet = response[0]
328 source = response[1][0]
329
330 if current_time > time_limit:
323331 raise socket.timeout
324332
325 reply = self._read_reply(
333 reply = self._parse_reply(
326334 packet=packet,
327 source=address,
328 reply_time=reply_time)
329
330 if (reply and request.id == reply.id and
335 source=source,
336 current_time=current_time)
337
338 if (reply and not request or
339 reply and request.id == reply.id and
331340 request.sequence == reply.sequence):
332341 return reply
333342
334343 except socket.timeout:
335 raise TimeoutExceeded(request.timeout)
344 raise TimeoutExceeded(timeout)
336345
337346 except OSError as err:
338347 raise ICMPSocketError(str(err))
342351 Close the socket. It cannot be used after this call.
343352
344353 '''
345 if self._socket:
346 self._socket.close()
347 self._socket = None
354 if self._sock:
355 self._sock.close()
356 self._sock = None
357
358 @property
359 def blocking(self):
360 '''
361 Indicate whether the socket is in blocking mode.
362 Return a `boolean`.
363
364 '''
365 return self._sock.getblocking()
366
367 @blocking.setter
368 def blocking(self, enable):
369 return self._sock.setblocking(enable)
370
371 @property
372 def address(self):
373 '''
374 The IP address from which the socket listens and sends packets.
375 Return `None` if the socket listens on all interfaces.
376
377 '''
378 return self._address
379
380 @property
381 def is_privileged(self):
382 '''
383 Indicate whether the socket is running in privileged mode.
384 Return a `boolean`.
385
386 '''
387 return self._privileged
348388
349389 @property
350390 def is_closed(self):
351391 '''
352 Indicate whether the socket is closed. Return a `boolean`.
353
354 '''
355 return self._socket is None
392 Indicate whether the socket is closed.
393 Return a `boolean`.
394
395 '''
396 return self._sock is None
397
398
399 # Echo Request and Echo Reply messages RFC 792
400 #
401 # 0 1 2 3
402 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
403 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
404 # | Type | Code | Checksum |
405 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
406 # | Identifier | Sequence Number |
407 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
408 # | Data ...
409 # +-+-+-+-+-
410 #
411 # ICMPv4 Error message RFC 792
412 #
413 # 0 1 2 3
414 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
415 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
416 # | Type | Code | Checksum |
417 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
418 # | Unused / Depends on the error |
419 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
420 # | Internet Header + 64 bits of Original Data Datagram |
421 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
356422
357423
358424 class ICMPv4Socket(ICMPSocket):
359425 '''
360 Socket for sending and receiving ICMPv4 packets.
361
362 :raises SocketPermissionError: If the permissions are insufficient
363 to create the socket.
426 Class for sending and receiving ICMPv4 packets.
427
428 :type address: str, optional
429 :param address: The IP address from which you want to listen and
430 send packets. By default, the socket listens on all interfaces.
431
432 :type privileged: bool, optional
433 :param privileged: When this option is enabled, the socket fully
434 manages the exchanges and the structure of the ICMP packets.
435 Disable this option if you want to instantiate and use the
436 socket without root privileges and let the kernel handle ICMP
437 headers. Default to True.
438 Only available on Unix systems. Ignored on Windows.
439
440 :raises SocketPermissionError: If the privileges are insufficient to
441 create the socket.
442 :raises SocketAddressError: If the requested address cannot be
443 assigned to the socket.
444 :raises ICMPSocketError: If another error occurs while creating the
445 socket.
364446
365447 '''
366 def __init__(self):
367 super().__init__(ICMPv4Config)
448 __slots__ = '_sock', '_address', '_privileged'
449
450 _IP_VERSION = 4
451 _ICMP_HEADER_OFFSET = 20
452 _ICMP_HEADER_REAL_OFFSET = 20
453
454 _ICMP_CODE_OFFSET = _ICMP_HEADER_OFFSET + 1
455 _ICMP_CHECKSUM_OFFSET = _ICMP_HEADER_OFFSET + 2
456 _ICMP_ID_OFFSET = _ICMP_HEADER_OFFSET + 4
457 _ICMP_SEQUENCE_OFFSET = _ICMP_HEADER_OFFSET + 6
458 _ICMP_PAYLOAD_OFFSET = _ICMP_HEADER_OFFSET + 8
459
460 _ICMP_ECHO_REQUEST = 8
461 _ICMP_ECHO_REPLY = 0
462
463 def _create_socket(self, type):
464 '''
465 Create and return a new socket.
466
467 '''
468 return socket.socket(
469 family=socket.AF_INET,
470 type=type,
471 proto=socket.IPPROTO_ICMP)
472
473 def _set_ttl(self, ttl):
474 '''
475 Set the time to live of every IP packet originating from this
476 socket.
477
478 '''
479 self._sock.setsockopt(
480 socket.IPPROTO_IP,
481 socket.IP_TTL,
482 ttl)
483
484 def _set_traffic_class(self, traffic_class):
485 '''
486 Set the DS Field (formerly TOS) of every IP packet originating
487 from this socket.
488
489 Only available on Unix systems. Ignored on Windows.
490
491 '''
492 # Not available on Windows
493 if PLATFORM_WINDOWS:
494 return
495
496 self._sock.setsockopt(
497 socket.IPPROTO_IP,
498 socket.IP_TOS,
499 traffic_class)
368500
369501 @property
370502 def broadcast(self):
377509 icmp_socket.broadcast = True
378510
379511 '''
380 return self._socket.broadcast
512 return self._sock.getsockopt(
513 socket.SOL_SOCKET,
514 socket.SO_BROADCAST) > 0
381515
382516 @broadcast.setter
383 def broadcast(self, allow):
384 self._socket.broadcast = allow
517 def broadcast(self, enable):
518 self._sock.setsockopt(
519 socket.SOL_SOCKET,
520 socket.SO_BROADCAST,
521 enable)
522
523
524 # Echo Request and Echo Reply messages RFC 4443
525 #
526 # 0 1 2 3
527 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
528 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
529 # | Type | Code | Checksum |
530 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
531 # | Identifier | Sequence Number |
532 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
533 # | Data ...
534 # +-+-+-+-+-
535 #
536 # ICMPv6 Error message RFC 4443
537 #
538 # 0 1 2 3
539 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
540 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
541 # | Type | Code | Checksum |
542 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
543 # | Unused / Depends on the error |
544 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
545 # | Original packet without exceed the minimum IPv6 MTU |
546 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
547
548 # Windows IPv6 compatibility
549 if PLATFORM_WINDOWS:
550 socket.IPPROTO_IPV6 = 41
551 socket.IPPROTO_ICMPV6 = 58
385552
386553
387554 class ICMPv6Socket(ICMPSocket):
388555 '''
389 Socket for sending and receiving ICMPv6 packets.
390
391 :raises SocketPermissionError: If the permissions are insufficient
392 to create the socket.
556 Class for sending and receiving ICMPv6 packets.
557
558 :type address: str, optional
559 :param address: The IP address from which you want to listen and
560 send packets. By default, the socket listens on all interfaces.
561
562 :type privileged: bool, optional
563 :param privileged: When this option is enabled, the socket fully
564 manages the exchanges and the structure of the ICMP packets.
565 Disable this option if you want to instantiate and use the
566 socket without root privileges and let the kernel handle ICMP
567 headers. Default to True.
568 Only available on Unix systems. Ignored on Windows.
569
570 :raises SocketPermissionError: If the privileges are insufficient to
571 create the socket.
572 :raises SocketAddressError: If the requested address cannot be
573 assigned to the socket.
574 :raises ICMPSocketError: If another error occurs while creating the
575 socket.
393576
394577 '''
395 def __init__(self):
396 super().__init__(ICMPv6Config)
578 __slots__ = '_sock', '_address', '_privileged'
579
580 _IP_VERSION = 6
581 _ICMP_HEADER_OFFSET = 0
582 _ICMP_HEADER_REAL_OFFSET = 40
583
584 _ICMP_CODE_OFFSET = _ICMP_HEADER_OFFSET + 1
585 _ICMP_CHECKSUM_OFFSET = _ICMP_HEADER_OFFSET + 2
586 _ICMP_ID_OFFSET = _ICMP_HEADER_OFFSET + 4
587 _ICMP_SEQUENCE_OFFSET = _ICMP_HEADER_OFFSET + 6
588 _ICMP_PAYLOAD_OFFSET = _ICMP_HEADER_OFFSET + 8
589
590 _ICMP_ECHO_REQUEST = 128
591 _ICMP_ECHO_REPLY = 129
592
593 def _create_socket(self, type):
594 '''
595 Create and return a new socket.
596
597 '''
598 return socket.socket(
599 family=socket.AF_INET6,
600 type=type,
601 proto=socket.IPPROTO_ICMPV6)
602
603 def _set_ttl(self, ttl):
604 '''
605 Set the time to live of every IP packet originating from this
606 socket.
607
608 '''
609 # Not available on macOS when the privileged param. is disabled
610 if PLATFORM_MACOS and not self._privileged:
611 return
612
613 self._sock.setsockopt(
614 socket.IPPROTO_IPV6,
615 socket.IPV6_UNICAST_HOPS,
616 ttl)
617
618 def _set_traffic_class(self, traffic_class):
619 '''
620 Set the Traffic Class field of every IP packet originating from
621 this socket.
622
623 Only available on Unix systems. Ignored on Windows.
624
625 '''
626 # Not available on Windows
627 if PLATFORM_WINDOWS:
628 return
629
630 # Not available on macOS when the privileged param. is disabled
631 if PLATFORM_MACOS and not self._privileged:
632 return
633
634 self._sock.setsockopt(
635 socket.IPPROTO_IPV6,
636 socket.IPV6_TCLASS,
637 traffic_class)
638
639
640 class AsyncSocket:
641 '''
642 A wrapper for ICMP sockets which makes them asynchronous.
643
644 :type icmp_sock: ICMPSocket
645 :param icmp_sock: An ICMP socket. Once the wrapper is instantiated,
646 this socket should no longer be used directly.
647
648 '''
649 __slots__ = '_icmp_sock'
650
651 def __init__(self, icmp_sock):
652 self._icmp_sock = icmp_sock
653 self._icmp_sock.blocking = False
654
655 def __getattr__(self, name):
656 '''
657 Return the specified attribute of the underlying ICMP socket.
658
659 '''
660 if not self._icmp_sock:
661 raise SocketUnavailableError
662
663 return getattr(self._icmp_sock, name)
664
665 def __enter__(self):
666 '''
667 Return this object.
668
669 '''
670 return self
671
672 def __exit__(self, type, value, traceback):
673 '''
674 Call the `close` method.
675
676 '''
677 self.close()
678
679 def __del__(self):
680 '''
681 Call the `close` method.
682
683 '''
684 self.close()
685
686 async def receive(self, request=None, timeout=2):
687 '''
688 Receive an ICMP reply message from the socket.
689
690 This method can be called multiple times if you expect several
691 responses as with a broadcast address.
692
693 This method is non-blocking.
694
695 :type request: ICMPRequest, optional
696 :param request: The ICMP request to use to match the response.
697 By default, all ICMP packets arriving on the socket are
698 returned.
699
700 :type timeout: int or float, optional
701 :param timeout: The maximum waiting time for receiving the
702 response in seconds. Default to 2.
703
704 :rtype: ICMPReply
705 :returns: An `ICMPReply` object representing the response of the
706 desired destination or an upstream gateway. See the
707 `ICMPReply` class for details.
708 Unlike the `reveive` method of synchronous ICMP sockets, the
709 returned `ICMPReply` object does not specify the IP address
710 of the host that replied to the request message.
711
712 :raises TimeoutExceeded: If no response is received before the
713 timeout specified in parameters.
714 :raises SocketUnavailableError: If the socket is closed.
715 :raises ICMPSocketError: If another error occurs while receiving.
716
717 '''
718 if not self._icmp_sock or not self._icmp_sock._sock:
719 raise SocketUnavailableError
720
721 loop = asyncio.get_running_loop()
722 time_limit = time() + timeout
723 remaining_time = timeout
724
725 try:
726 while True:
727 packet = await asyncio.wait_for(
728 loop.sock_recv(self._icmp_sock._sock, 1024),
729 remaining_time)
730
731 current_time = time()
732
733 if current_time > time_limit:
734 raise asyncio.TimeoutError
735
736 reply = self._parse_reply(
737 packet=packet,
738 source=None,
739 current_time=current_time)
740
741 if (reply and not request or
742 reply and request.id == reply.id and
743 request.sequence == reply.sequence):
744 return reply
745
746 remaining_time = time_limit - current_time
747
748 except asyncio.TimeoutError:
749 raise TimeoutExceeded(timeout)
750
751 except OSError as err:
752 raise ICMPSocketError(str(err))
753
754 finally:
755 if isinstance(loop, asyncio.SelectorEventLoop):
756 loop.remove_reader(self._icmp_sock._sock)
757
758 def detach(self):
759 '''
760 Detach the socket from the wrapper and return it. The wrapper
761 cannot be used after this call but the socket can be reused for
762 other purposes.
763
764 '''
765 icmp_sock = self._icmp_sock
766
767 if self._icmp_sock:
768 self._icmp_sock = None
769
770 return icmp_sock
771
772 def close(self):
773 '''
774 Detach the underlying socket from the wrapper and close it. Both
775 cannot be used after this call.
776
777 '''
778 if self._icmp_sock:
779 self.detach().close()
780
781 @property
782 def is_closed(self):
783 '''
784 Indicate whether the underlying socket is closed or detached
785 from this wrapper. Return a `boolean`.
786
787 '''
788 return self._icmp_sock is None
11 icmplib
22 ~~~~~~~
33
4 The power to forge ICMP packets and do ping and traceroute.
5
46 https://github.com/ValentinBELYN/icmplib
57
6 :copyright: Copyright 2017-2020 Valentin BELYN.
8 :copyright: Copyright 2017-2021 Valentin BELYN.
79 :license: GNU LGPLv3, see the LICENSE for details.
810
911 ~~~~~~~
2729
2830 from .sockets import ICMPv4Socket, ICMPv6Socket
2931 from .models import ICMPRequest, Hop
30 from .exceptions import *
31 from .utils import PID, resolve, is_ipv6_address
32
33
34 def traceroute(address, count=3, interval=0.05, timeout=2, id=PID,
35 traffic_class=0, max_hops=30, fast_mode=False, **kwargs):
32 from .exceptions import TimeExceeded, ICMPLibError
33 from .utils import *
34
35
36 def traceroute(address, count=2, interval=0.05, timeout=2, first_hop=1,
37 max_hops=30, fast=False, id=None, source=None, family=None,
38 **kwargs):
3639 '''
3740 Determine the route to a destination host.
3841
3942 The Internet is a large and complex aggregation of network hardware,
4043 connected together by gateways. Tracking the route one's packets
41 follow can be difficult. This function utilizes the IP protocol
42 time to live field and attempts to elicit an ICMP TIME_EXCEEDED
43 response from each gateway along the path to some host.
44 follow can be difficult. This function uses the IP protocol time to
45 live field and attempts to elicit an ICMP Time Exceeded response
46 from each gateway along the path to some host.
47
48 This function requires root privileges to run.
4449
4550 :type address: str
46 :param address: The destination IP address.
47
48 :type count: int
49 :param count: (Optional) The number of ping to perform per hop.
50
51 :type interval: int or float
52 :param interval: (Optional) The interval in seconds between sending
53 each packet.
54
55 :type timeout: int or float
56 :param timeout: (Optional) The maximum waiting time for receiving
57 a reply in seconds.
58
59 :type id: int
60 :param id: (Optional) The identifier of the request. Used to match
61 the reply with the request. In practice, a unique identifier is
62 used for every ping process.
63
64 :type traffic_class: int
65 :param traffic_class: (Optional) The traffic class of packets.
66 Provides a defined level of service to packets by setting the
67 DS Field (formerly TOS) or the Traffic Class field of IP
68 headers. Packets are delivered with the minimum priority by
69 default (Best-effort delivery).
51 :param address: The IP address, hostname or FQDN of the host to
52 reach. For deterministic behavior, prefer to use an IP address.
53
54 :type count: int, optional
55 :param count: The number of ping to perform per hop. Default to 2.
56
57 :type interval: int or float, optional
58 :param interval: The interval in seconds between sending each packet.
59 Default to 0.05.
60
61 :type timeout: int or float, optional
62 :param timeout: The maximum waiting time for receiving a reply in
63 seconds. Default to 2.
64
65 :type first_hop: int, optional
66 :param first_hop: The initial time to live value used in outgoing
67 probe packets. Default to 1.
68
69 :type max_hops: int, optional
70 :param max_hops: The maximum time to live (max number of hops) used
71 in outgoing probe packets. Default to 30.
72
73 :type fast: bool, optional
74 :param fast: When this option is enabled and an intermediate router
75 has been reached, skip to the next hop rather than perform
76 additional requests. The `count` parameter then becomes the
77 maximum number of requests in the event of no response.
78 Default to False.
79
80 :type id: int, optional
81 :param id: The identifier of ICMP requests. Used to match the
82 responses with requests. In practice, a unique identifier should
83 be used for every traceroute process. The library handles this
84 identifier itself by default.
85
86 :type source: str, optional
87 :param source: The IP address from which you want to send packets.
88 By default, the interface is automatically chosen according to
89 the specified destination.
90
91 :type family: int, optional
92 :param family: The address family if a hostname or FQDN is specified.
93 Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default,
94 this function searches for IPv4 addresses first before searching
95 for IPv6 addresses.
96
97 Advanced (**kwags):
98
99 :type payload: bytes, optional
100 :param payload: The payload content in bytes. A random payload is
101 used by default.
102
103 :type payload_size: int, optional
104 :param payload_size: The payload size. Ignored when the `payload`
105 parameter is set. Default to 56.
106
107 :type traffic_class: int, optional
108 :param traffic_class: The traffic class of ICMP packets.
109 Provides a defined level of service to packets by setting the DS
110 Field (formerly TOS) or the Traffic Class field of IP headers.
111 Packets are delivered with the minimum priority by default
112 (Best-effort delivery).
70113 Intermediate routers must be able to support this feature.
71114 Only available on Unix systems. Ignored on Windows.
72115
73 :type max_hops: int
74 :param max_hops: (Optional) The maximum time to live (max number of
75 hops) used in outgoing probe packets.
76
77 :type fast_mode: bool
78 :param fast_mode: (Optional) When this option is enabled and an
79 intermediate router has been reached, skip to the next hop
80 rather than perform additional requests. The `count` parameter
81 then becomes the maximum number of requests in case of no
82 responses.
83
84 :param **kwargs: (Optional) Advanced use: arguments passed to
85 `ICMPRequest` objects.
86
87 :rtype: list of Hop
116 :rtype: list[Hop]
88117 :returns: A list of `Hop` objects representing the route to the
89 desired host. The list is sorted in ascending order according
90 to the distance (in terms of hops) that separates the remote
91 host from the current machine.
92
93 :raises SocketPermissionError: If the permissions are insufficient
94 to create a socket.
118 desired destination. The list is sorted in ascending order
119 according to the distance, in terms of hops, that separates the
120 remote host from the current machine. Gateways that do not
121 respond to requests are not added to this list.
122
123 :raises NameLookupError: If you pass a hostname or FQDN in
124 parameters and it does not exist or cannot be resolved.
125 :raises SocketPermissionError: If the privileges are insufficient to
126 create the socket.
127 :raises SocketAddressError: If the source address cannot be assigned
128 to the socket.
129 :raises ICMPSocketError: If another error occurs. See the
130 `ICMPv4Socket` or `ICMPv6Socket` class for details.
95131
96132 Usage::
97133
101137
102138 >>> for hop in hops:
103139 ... if last_distance + 1 != hop.distance:
104 ... print('Some routers are not responding')
140 ... print('Some gateways are not responding')
105141 ...
106142 ... print(f'{hop.distance} {hop.address} {hop.avg_rtt} ms')
107143 ...
108144 ... last_distance = hop.distance
109 ...
110 1 10.0.0.1 5.196 ms
111 2 194.149.169.49 7.552 ms
112 3 194.149.166.54 12.21 ms
113 * Some routers are not responding
114 5 212.73.205.22 22.15 ms
115 6 1.1.1.1 13.59 ms
145
146 1 10.0.0.1 5.196 ms
147 2 194.149.169.49 7.552 ms
148 3 194.149.166.54 12.21 ms
149 * Some gateways are not responding
150 5 212.73.205.22 22.15 ms
151 6 1.1.1.1 13.59 ms
116152
117153 See the `Hop` class for details.
118154
119155 '''
120 address = resolve(address)
156 if is_hostname(address):
157 address = resolve(address, family)[0]
121158
122159 if is_ipv6_address(address):
123 socket = ICMPv6Socket()
124
160 _Socket = ICMPv6Socket
125161 else:
126 socket = ICMPv4Socket()
127
128 ttl = 1
162 _Socket = ICMPv4Socket
163
164 id = id or unique_identifier()
165 ttl = first_hop
129166 host_reached = False
130167 hops = []
131168
132 while not host_reached and ttl <= max_hops:
133 hop_address = None
134 packets_sent = 0
135 packets_received = 0
136
137 min_rtt = float('inf')
138 avg_rtt = 0.0
139 max_rtt = 0.0
140
141 for sequence in range(count):
142 request = ICMPRequest(
143 destination=address,
144 id=id,
145 sequence=sequence,
146 timeout=timeout,
147 ttl=ttl,
148 traffic_class=traffic_class,
149 **kwargs)
150
151 try:
152 socket.send(request)
153 packets_sent += 1
154
155 reply = socket.receive()
156 reply.raise_for_status()
157 host_reached = True
158
159 except TimeExceeded:
160 sleep(interval)
161
162 except ICMPLibError:
163 continue
164
165 hop_address = reply.source
166 packets_received += 1
167
168 round_trip_time = (reply.time - request.time) * 1000
169 avg_rtt += round_trip_time
170 min_rtt = min(round_trip_time, min_rtt)
171 max_rtt = max(round_trip_time, max_rtt)
172
173 if fast_mode:
174 break
175
176 if packets_received:
177 avg_rtt /= packets_received
178
179 hop = Hop(
180 address=hop_address,
181 min_rtt=min_rtt,
182 avg_rtt=avg_rtt,
183 max_rtt=max_rtt,
184 packets_sent=packets_sent,
185 packets_received=packets_received,
186 distance=ttl)
187
188 hops.append(hop)
189
190 ttl += 1
191
192 socket.close()
169 with _Socket(source) as sock:
170 while not host_reached and ttl <= max_hops:
171 reply = None
172 packets_sent = 0
173 rtts = []
174
175 for sequence in range(count):
176 request = ICMPRequest(
177 destination=address,
178 id=id,
179 sequence=sequence,
180 ttl=ttl,
181 **kwargs)
182
183 try:
184 sock.send(request)
185 packets_sent += 1
186
187 reply = sock.receive(request, timeout)
188 rtt = (reply.time - request.time) * 1000
189 rtts.append(rtt)
190
191 reply.raise_for_status()
192 host_reached = True
193
194 except TimeExceeded:
195 sleep(interval)
196
197 except ICMPLibError:
198 break
199
200 if reply:
201 hop = Hop(
202 address=reply.source,
203 packets_sent=packets_sent,
204 rtts=rtts,
205 distance=ttl)
206
207 hops.append(hop)
208
209 ttl += 1
193210
194211 return hops
11 icmplib
22 ~~~~~~~
33
4 The power to forge ICMP packets and do ping and traceroute.
5
46 https://github.com/ValentinBELYN/icmplib
57
6 :copyright: Copyright 2017-2020 Valentin BELYN.
8 :copyright: Copyright 2017-2021 Valentin BELYN.
79 :license: GNU LGPLv3, see the LICENSE for details.
810
911 ~~~~~~~
2325 <https://www.gnu.org/licenses/>.
2426 '''
2527
26 import socket
28 import socket, asyncio
29
30 from threading import Lock
2731 from sys import platform
2832 from os import getpid
2933 from re import match
3034 from random import choices
3135
36 from .exceptions import NameLookupError
37
3238
3339 PID = getpid()
40 PLATFORM_LINUX = platform == 'linux'
41 PLATFORM_MACOS = platform == 'darwin'
3442 PLATFORM_WINDOWS = platform == 'win32'
43
44 _lock_id = Lock()
45 _current_id = PID
3546
3647
3748 def random_byte_message(size):
3950 Generate a random byte sequence of the specified size.
4051
4152 '''
42 bytes_available = (
53 sequence = choices(
4354 b'abcdefghijklmnopqrstuvwxyz'
4455 b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
45 b'1234567890'
46 )
56 b'1234567890', k=size)
4757
48 return bytes(
49 choices(bytes_available, k=size)
50 )
58 return bytes(sequence)
5159
5260
53 def resolve(name):
61 def unique_identifier():
5462 '''
55 Resolve a hostname or FQDN into an IP address. If several IP
56 addresses are available, only the first one is returned.
57
58 This function searches for IPv4 addresses first for compatibility
59 reasons before searching for IPv6 addresses.
60
61 If no address is found, the name specified in parameter is
62 returned so as not to impact the operation of other functions. This
63 behavior may change in future versions of icmplib.
63 Generate a unique identifier between 0 and 65535.
64 The first number generated will be equal to the PID + 1.
6465
6566 '''
66 if is_ipv4_address(name) or is_ipv6_address(name):
67 return name
67 global _current_id
6868
69 with _lock_id:
70 _current_id += 1
71 _current_id &= 0xffff
72
73 return _current_id
74
75
76 def resolve(name, family=None):
77 '''
78 Resolve a hostname or FQDN to an IP address. Depending on the name
79 specified in parameters, several IP addresses may be returned.
80
81 This function relies on the DNS name server configured on your
82 operating system.
83
84 :type name: str
85 :param name: A hostname or a Fully Qualified Domain Name (FQDN).
86
87 :type family: int, optional
88 :param family: The address family. Can be set to `4` for IPv4 or `6`
89 for IPv6 addresses. By default, this function searches for IPv4
90 addresses first for compatibility reasons (A DNS lookup) before
91 searching for IPv6 addresses (AAAA DNS lookup).
92
93 :rtype: list[str]
94 :returns: A list of IP addresses corresponding to the name passed as
95 a parameter.
96
97 :raises NameLookupError: If the requested name does not exist or
98 cannot be resolved.
99
100 '''
69101 try:
70 return socket.getaddrinfo(
102 if family == 6:
103 _family = socket.AF_INET6
104 else:
105 _family = socket.AF_INET
106
107 lookup = socket.getaddrinfo(
71108 host=name,
72109 port=None,
73 family=socket.AF_INET,
74 type=socket.SOCK_DGRAM
75 )[0][4][0]
110 family=_family,
111 type=socket.SOCK_DGRAM)
112
113 return [address[4][0] for address in lookup]
76114
77115 except OSError:
78 pass
116 if not family:
117 return resolve(name, 6)
79118
119 raise NameLookupError(name)
120
121
122 async def async_resolve(name, family=None):
123 '''
124 Resolve a hostname or FQDN to an IP address. Depending on the name
125 specified in parameters, several IP addresses may be returned.
126
127 This function relies on the DNS name server configured on your
128 operating system.
129
130 This function is non-blocking.
131
132 :type name: str
133 :param name: A hostname or a Fully Qualified Domain Name (FQDN).
134
135 :type family: int, optional
136 :param family: The address family. Can be set to `4` for IPv4 or `6`
137 for IPv6 addresses. By default, this function searches for IPv4
138 addresses first for compatibility reasons (A DNS lookup) before
139 searching for IPv6 addresses (AAAA DNS lookup).
140
141 :rtype: list[str]
142 :returns: A list of IP addresses corresponding to the name passed as
143 a parameter.
144
145 :raises NameLookupError: If the requested name does not exist or
146 cannot be resolved.
147
148 '''
80149 try:
81 return socket.getaddrinfo(
150 if family == 6:
151 _family = socket.AF_INET6
152 else:
153 _family = socket.AF_INET
154
155 loop = asyncio.get_running_loop()
156
157 lookup = await loop.getaddrinfo(
82158 host=name,
83159 port=None,
84 family=socket.AF_INET6,
85 type=socket.SOCK_DGRAM
86 )[0][4][0]
160 family=_family,
161 type=socket.SOCK_DGRAM)
162
163 return [address[4][0] for address in lookup]
87164
88165 except OSError:
89 return name
166 if not family:
167 return await async_resolve(name, 6)
168
169 raise NameLookupError(name)
170
171
172 def is_hostname(name):
173 '''
174 Indicate whether the specified name is a hostname or an FQDN.
175 Return a `boolean`.
176
177 '''
178 pattern = r'(?i)^([a-z0-9-]+|([a-z0-9_-]+[.])+[a-z]+)$'
179 return match(pattern, name) is not None
90180
91181
92182 def is_ipv4_address(address):
95185 Return a `boolean`.
96186
97187 '''
98 return match(
99 r'^([0-9]{1,3}[.]){3}[0-9]{1,3}$',
100 address
101 ) is not None
188 pattern = r'^([0-9]{1,3}[.]){3}[0-9]{1,3}$'
189 return match(pattern, address) is not None
102190
103191
104192 def is_ipv6_address(address):
00 [metadata]
11 name = icmplib
2 version = 1.2.2
3 description = Easily forge ICMP packets and make your own ping and traceroute.
4 keywords = pure, implementation, icmp, sockets, ping, multiping, traceroute, ipv4, ipv6, python3
2 version = 3.0.2
3 description = The power to forge ICMP packets and do ping and traceroute.
4 keywords = icmp, sockets, ping, multiping, traceroute, async, asyncio, ipv4, ipv6, python, python3
55
66 author = Valentin BELYN
77 author_email = [email protected]
1818 Intended Audience :: Developers
1919 License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
2020 Operating System :: OS Independent
21 Programming Language :: Python
2122 Programming Language :: Python :: 3
2223 Programming Language :: Python :: 3 :: Only
23 Programming Language :: Python :: 3.6
2424 Programming Language :: Python :: 3.7
2525 Programming Language :: Python :: 3.8
26 Programming Language :: Python :: 3.9
27 Programming Language :: Python :: 3.10
2628 Topic :: Software Development :: Libraries
2729 Topic :: Software Development :: Libraries :: Python Modules
2830
2931 [options]
3032 packages = icmplib
31 python_requires = >=3.6
33 python_requires = >=3.7
11 icmplib
22 ~~~~~~~
33
4 The power to forge ICMP packets and do ping and traceroute.
5
46 https://github.com/ValentinBELYN/icmplib
57
6 :copyright: Copyright 2017-2020 Valentin BELYN.
8 :copyright: Copyright 2017-2021 Valentin BELYN.
79 :license: GNU LGPLv3, see the LICENSE for details.
810
911 ~~~~~~~
2527
2628 from setuptools import setup
2729
28 setup()
30 setup(name='icmplib')