Codebase list python-icmplib / 960508f
Import upstream version 2.1.1 Kali Janitor 3 years ago
20 changed file(s) with 1659 addition(s) and 1048 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 ## [v2.1.1](https://github.com/ValentinBELYN/icmplib/releases/tag/v2.1.1) - 2021-03-21
5 - Rollback changes made to the `traceroute` function due to a bug.
6
7 ## [v2.1.0](https://github.com/ValentinBELYN/icmplib/releases/tag/v2.1.0) - 2021-03-20
8 - Add a `family` parameter to the `resolve` function to define the address family.
9 - Improve the reliability of the results of the `traceroute` function.
10
11 > This version is the last of the 2.x branch. See you soon for the release of icmplib 3.0!
12
13 ## [v2.0.2](https://github.com/ValentinBELYN/icmplib/releases/tag/v2.0.2) - 2021-02-07
14 - Rename the default branch from `master` to `main`.
15 - Fix a bug preventing the `traceroute` function to work with IPv6 addresses (part 2).
16 - Add more details about the `privileged` parameter in the `README` file (part 2).
17
18 ## [v2.0.1](https://github.com/ValentinBELYN/icmplib/releases/tag/v2.0.1) - 2020-12-12
19 - Handle `EACCES` errors at sockets level.
20 - Fix a bug preventing the `traceroute` function to work with IPv6 addresses.
21 - Add some details about the `privileged` parameter in the `README` file.
22
23 ## [v2.0.0](https://github.com/ValentinBELYN/icmplib/releases/tag/v2.0.0) - 2020-11-15
24 icmplib 2.0 is here! :tada:
25
26 Here is an overview of the improvements:
27 - All the foundations of the library have been completely reworked to make it even faster and simplify future developments.
28 - You can now use the library without root privileges. Remember to disable the `privileged` parameter on functions and sockets.
29 - The `multiping` function has been rewritten to use only one thread instead of as many threads as hosts to reach. This function will be up to 10 times faster and up to 2 times more memory efficient.
30 - You can set a source IP address for sending your ICMP packets.
31 - The `traceroute` function now has a `first_hop` parameter to specify the initial time to live value.
32 - Two new exceptions have been added: `NameLookupError` and `SocketAddressError`
33 - Compatibility with Linux, macOS and Windows has been improved.
34 - Docstrings, examples and documentation have been updated.
35
36 And more!
37 - The `receive` method of sockets can receive all incoming packets.
38 - The new `BufferedSocket` class (experimental) can read and classify incoming ICMP packets into a buffer, in real time. Useful if you want to send several ICMP packets consecutively without waiting for a response between each sending.
39 - Sockets throw new exceptions during instantiation, sending and receiving.
40 - The `resolve` function now raises a `NameLookupError` if the requested name does not exist or cannot be resolved.
41 - Compatibility with existing programs is maintained.
342
443 ## [v1.2.2](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.2.2) - 2020-10-10
544 - Add support for hostnames and FQDN resolution to IPv6 addresses.
00 <div align="center">
11 <br>
2 <img src="media/icmplib-logo.png" height="125" width="100" alt="icmplib">
2 <img src="media/icmplib-logo-2.0.png" height="190" width="100" alt="icmplib">
33 <br>
44 <br>
55
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>
106 <br>
11 <br>
12
137 <div>
148 <a href="#features">Features</a>&nbsp;&nbsp;&nbsp;
159 <a href="#installation">Installation</a>&nbsp;&nbsp;&nbsp;
2115 <a href="#license">License</a>
2216 </div>
2317 <br>
24 <br>
2518
2619 <pre>icmplib is a brand new and modern implementation of the ICMP protocol in Python.
2720 Use the built-in functions or build your own, you have the choice!
2821
29 <strong>Root privileges are required to use this library (raw sockets).</strong></pre>
22 <a href="#how-to-use-the-library-without-root-privileges">- You can now use this library without root privileges -</a></pre>
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>
3531
3632 - :deciduous_tree: **Ready-to-use:** icmplib offers ready-to-use functions such as the most popular ones: `ping`, `multiping` and `traceroute`.
3733 - :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.
34 - :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!
35 - :zap: **Powerful:** Use the library without root privileges, set the traffic class of ICMP packets, customize their payload and more!
36 - :nut_and_bolt: **Evolutive:** Easily build your own classes and functions with `ICMPv4` and `ICMPv6` sockets.
4037 - :fire: **Seamless integration of IPv6:** Use IPv6 the same way you use IPv4. Automatic detection is done without impacting performance.
4138 - :rainbow: **Broadcast support** (you must use the `ICMPv4Socket` class to enable it).
4239 - :beer: **Support of all operating systems.** Tested on Linux, macOS and Windows.
4643
4744 ## Installation
4845
49 Install, upgrade and uninstall icmplib with these commands:
46 The recommended way to install or upgrade icmplib is to use `pip3`:
5047
5148 ```shell
5249 $ pip3 install icmplib
5350 $ 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):
51 ```
52
53 *icmplib requires Python 3.6 or later.*
54
55 To import icmplib into your project (only import what you need):
6056
6157 ```python
6258 # For simple use
63 from icmplib import ping, multiping, traceroute, Host, Hop
59 from icmplib import ping, multiping, traceroute, resolve, Host, Hop
6460
6561 # For advanced use (sockets)
6662 from icmplib import ICMPv4Socket, ICMPv6Socket, ICMPRequest, ICMPReply
6763
6864 # Exceptions
69 from icmplib import ICMPLibError, ICMPSocketError, SocketPermissionError
65 from icmplib import ICMPLibError, NameLookupError, ICMPSocketError
66 from icmplib import SocketAddressError, SocketPermissionError
7067 from icmplib import SocketUnavailableError, SocketBroadcastError, TimeoutExceeded
7168 from icmplib import ICMPError, DestinationUnreachable, TimeExceeded
7269 ```
7673 ## Built-in functions
7774
7875 ### 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)
76 Send ICMP Echo Request packets to a network host.
77
78 ```python
79 ping(address, count=4, interval=1, timeout=2, id=PID, source=None, privileged=True, **kwargs)
8480 ```
8581
8682 #### Parameters
8783 - `address`
8884
89 The IP address of the gateway or host to which the message should be sent.
85 The IP address, hostname or FQDN of the host to which messages should be sent. For deterministic behavior, prefer to use an IP address.
9086
9187 - Type: `str`
9288
113109
114110 - `id`
115111
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.
112 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.
118113
119114 - Type: `int`
120115 - Default: `PID`
121116
122 - `**kwargs`
123
124 `Optional` Advanced use: arguments passed to `ICMPRequest` objects.
117 - `source`
118
119 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination.
120
121 - Type: `str`
122 - Default: `None`
123
124 - `privileged`
125
126 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.
127
128 *Only available on Unix systems. Ignored on Windows.*
129
130 - Type: `bool`
131 - Default: `True`
132
133 - `payload`
134
135 The payload content in bytes. A random payload is used by default.
136
137 - Type: `bytes`
138 - Default: `None`
139
140 - `payload_size`
141
142 The payload size. Ignored when the `payload` parameter is set.
143
144 - Type: `int`
145 - Default: `56`
146
147 - `traffic_class`
148
149 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.
150
151 *Only available on Unix systems. Ignored on Windows.*
152
153 - Type: `int`
154 - Default: `0`
125155
126156 #### 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`.
157 - A `Host` object containing statistics about the desired destination:<br>
158 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`, `packets_received`, `packet_loss`, `is_alive`
132159
133160 #### Exceptions
161 - `NameLookupError`
162
163 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
164
134165 - `SocketPermissionError`
135166
136 If the permissions are insufficient to create a socket.
167 If the privileges are insufficient to create the socket.
168
169 - `SocketAddressError`
170
171 If the source address cannot be assigned to the socket.
172
173 - `ICMPSocketError`
174
175 If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details.
137176
138177 #### Example
139178 ```python
140179 >>> host = ping('1.1.1.1', count=10, interval=0.2)
141180
142 >>> host.address # The IP address of the gateway or host
143 '1.1.1.1' # that responded to the request
181 >>> host.address # The IP address of the host that responded
182 '1.1.1.1' # to the request
144183
145184 >>> host.min_rtt # The minimum round-trip time
146185 12.2
167206 <br>
168207
169208 ### 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)
209 Send ICMP Echo Request packets to several network hosts.
210
211 This function relies on a single thread to send multiple packets simultaneously. If you mix IPv4 and IPv6 addresses, up to two threads are used.
212
213 ```python
214 multiping(addresses, count=2, interval=0.01, timeout=2, id=PID, source=None, privileged=True, **kwargs)
175215 ```
176216
177217 #### Parameters
178218 - `addresses`
179219
180 The IP addresses of the gateways or hosts to which messages should be sent.
220 The IP addresses of the hosts to which messages should be sent. Hostnames and FQDNs are not allowed. You can easily retrieve their IP address by calling the built-in `resolve` function.
181221
182222 - Type: `list of str`
183223
193233 The interval in seconds between sending each packet.
194234
195235 - Type: `int` or `float`
196 - Default: `1`
236 - Default: `0.01`
197237
198238 - `timeout`
199239
200 The maximum waiting time for receiving a reply in seconds.
240 The maximum waiting time for receiving all responses in seconds.
201241
202242 - Type: `int` or `float`
203243 - Default: `2`
204244
205245 - `id`
206246
207 The identifier of the requests. This identifier will be incremented by one for each destination.
247 The identifier of ICMP requests. Used to match the responses with requests. This identifier will be incremented by one for each destination. On Linux, this identifier is ignored when the `privileged` parameter is disabled.
208248
209249 - Type: `int`
210250 - Default: `PID`
211251
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.
252 - `source`
253
254 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination. This parameter should not be used if you are passing both IPv4 and IPv6 addresses to this function.
255
256 - Type: `str`
257 - Default: `None`
258
259 - `privileged`
260
261 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.
262
263 *Only available on Unix systems. Ignored on Windows.*
264
265 - Type: `bool`
266 - Default: `True`
267
268 - `payload`
269
270 The payload content in bytes. A random payload is used by default.
271
272 - Type: `bytes`
273 - Default: `None`
274
275 - `payload_size`
276
277 The payload size. Ignored when the `payload` parameter is set.
278
279 - Type: `int`
280 - Default: `56`
281
282 - `traffic_class`
283
284 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.
285
286 *Only available on Unix systems. Ignored on Windows.*
287
288 - Type: `int`
289 - Default: `0`
222290
223291 #### 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>
292 - A `list of Host` objects containing statistics about the desired destinations:<br>
293 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`, `packets_received`, `packet_loss`, `is_alive`
294
229295 The list is sorted in the same order as the addresses passed in parameters.
230296
231297 #### Exceptions
232298 - `SocketPermissionError`
233299
234 If the permissions are insufficient to create a socket.
300 If the privileges are insufficient to create the socket.
301
302 - `SocketAddressError`
303
304 If the source address cannot be assigned to the socket.
305
306 - `ICMPSocketError`
307
308 If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details.
235309
236310 #### Example
237311 ```python
256330 ### Traceroute
257331 Determine the route to a destination host.
258332
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)
333 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.
334
335 *This function requires root privileges to run.*
336
337 ```python
338 traceroute(address, count=2, interval=0.05, timeout=2, id=PID, first_hop=1, max_hops=30, source=None, fast=False, **kwargs)
264339 ```
265340
266341 #### Parameters
267342 - `address`
268343
269 The destination IP address.
344 The IP address, hostname or FQDN of the host to reach. For deterministic behavior, prefer to use an IP address.
270345
271346 - Type: `str`
272347
275350 The number of ping to perform per hop.
276351
277352 - Type: `int`
278 - Default: `3`
353 - Default: `2`
279354
280355 - `interval`
281356
293368
294369 - `id`
295370
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.
371 The identifier of ICMP requests. Used to match the responses with requests. In practice, a unique identifier should be used for every traceroute process.
298372
299373 - Type: `int`
300374 - Default: `PID`
301375
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`
376 - `first_hop`
377
378 The initial time to live value used in outgoing probe packets.
379
380 - Type: `int`
381 - Default: `1`
311382
312383 - `max_hops`
313384
316387 - Type: `int`
317388 - Default: `30`
318389
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.
390 - `source`
391
392 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination.
393
394 - Type: `str`
395 - Default: `None`
396
397 - `fast`
398
399 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.
322400
323401 - Type: `bool`
324402 - Default: `False`
325403
326 - `**kwargs`
327
328 `Optional` Advanced use: arguments passed to `ICMPRequest` objects.
404 - `payload`
405
406 The payload content in bytes. A random payload is used by default.
407
408 - Type: `bytes`
409 - Default: `None`
410
411 - `payload_size`
412
413 The payload size. Ignored when the `payload` parameter is set.
414
415 - Type: `int`
416 - Default: `56`
417
418 - `traffic_class`
419
420 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.
421
422 *Only available on Unix systems. Ignored on Windows.*
423
424 - Type: `int`
425 - Default: `0`
329426
330427 #### 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.
428 - 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`.
429
430 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.
334431
335432 #### Exceptions
433 - `NameLookupError`
434
435 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
436
336437 - `SocketPermissionError`
337438
338 If the permissions are insufficient to create a socket.
439 If the privileges are insufficient to create the socket.
440
441 - `SocketAddressError`
442
443 If the source address cannot be assigned to the socket.
444
445 - `ICMPSocketError`
446
447 If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details.
339448
340449 #### Example
341450 ```python
342451 >>> hops = traceroute('1.1.1.1')
343452
344 >>> print('Distance (ttl) Address Average round-trip time')
453 >>> print('Distance/TTL Address Average round-trip time')
345454 >>> last_distance = 0
346455
347456 >>> for hop in hops:
348457 ... if last_distance + 1 != hop.distance:
349 ... print('Some routers are not responding')
458 ... print('Some gateways are not responding')
350459 ...
351460 ... # See the Hop class for details
352461 ... print(f'{hop.distance} {hop.address} {hop.avg_rtt} ms')
354463 ... last_distance = hop.distance
355464 ...
356465
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
466 # Distance/TTL Address Average round-trip time
467 # 1 10.0.0.1 5.196 ms
468 # 2 194.149.169.49 7.552 ms
469 # 3 194.149.166.54 12.21 ms
470 # * Some gateways are not responding
471 # 5 212.73.205.22 22.15 ms
472 # 6 1.1.1.1 13.59 ms
364473 ```
365474
366475 <br>
367476
368477 ## ICMP sockets
369478
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 └─────────────────┘
479 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.
480
481 ```
482 ┌──────────────────┐
483 ┌─────────────────┐ send(...) │ ICMPv4Socket │ receive() ┌─────────────────┐
484 │ ICMPRequest │ ────────────> │ or │ ────────────> │ ICMPReply │
485 └─────────────────┘ │ ICMPv6Socket │ └─────────────────┘
486 └──────────────────┘
378487 ```
379488
380489 ### 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
490 A user-created object that represents an ICMP Echo Request.
491
492 ```python
493 ICMPRequest(destination, id, sequence, payload=None, payload_size=56, ttl=64, traffic_class=0)
494 ```
495
496 #### Parameters and properties
389497 - `destination`
390498
391 The IP address of the gateway or host to which the message should be sent.
499 The IP address of the host to which the message should be sent.
392500
393501 - Type: `str`
394502
395503 - `id`
396504
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.
505 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.
399506
400507 - Type: `int`
401508
402509 - `sequence`
403510
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.
511 The sequence number. Used to match the reply with the request. Typically, the sequence number is incremented for each packet sent during the process.
406512
407513 - Type: `int`
408514
420526 - Type: `int`
421527 - Default: `56`
422528
423 - `timeout`
424
425 The maximum waiting time for receiving a reply in seconds.
426
427 - Type: `int` or `float`
428 - Default: `2`
429
430529 - `ttl`
431530
432 The time to live of the packet in seconds.
531 The time to live of the packet in terms of hops.
433532
434533 - Type: `int`
435534 - Default: `64`
436535
437536 - `traffic_class`
438537
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>
538 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). Intermediate routers must be able to support this feature.
539
442540 *Only available on Unix systems. Ignored on Windows.*
443541
444542 - Type: `int`
445543 - Default: `0`
446544
447 #### Getters only
545 #### Properties only
448546 - `time`
449547
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.
548 The timestamp of the ICMP request. Initialized to zero when creating the request and replaced by the `send` method of `ICMPv4Socket` or `ICMPv6Socket` with the time of sending.
451549
452550 - Type: `float`
453551
454552 <br>
455553
456554 ### ICMPReply
457 A class that represents an ICMP reply. Generated from an `ICMPSocket` object (`ICMPv4Socket` or `ICMPv6Socket`).
458
459 #### Definition
555 A class that represents an ICMP reply. Generated from an ICMP socket (`ICMPv4Socket` or `ICMPv6Socket`).
556
460557 ```python
461558 ICMPReply(source, id, sequence, type, code, bytes_received, time)
462559 ```
463560
464 #### Parameters / Getters
561 #### Parameters and properties
465562 - `source`
466563
467564 The IP address of the gateway or host that composes the ICMP message.
470567
471568 - `id`
472569
473 The identifier of the request. Used to match the reply with the request.
570 The identifier of the reply. Used to match the reply with the request.
474571
475572 - Type: `int`
476573
482579
483580 - `type`
484581
485 The type of message.
582 The type of ICMP message.
486583
487584 - Type: `int`
488585
489586 - `code`
490587
491 The error code.
588 The ICMP error code.
492589
493590 - Type: `int`
494591
507604 #### Methods
508605 - `raise_for_status()`
509606
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*.
607 Throw an exception if the reply is not an ICMP Echo Reply. Otherwise, do nothing.
608
609 - Raises `DestinationUnreachable`: If the destination is unreachable for some reason.
610 - Raises `TimeExceeded`: If the time to live field of the ICMP request has reached zero.
611 - Raises `ICMPError`: Raised for any other type and ICMP error code, except ICMP Echo Reply messages.
518612
519613 <br>
520614
521615 ### ICMPv4Socket
522 Socket for sending and receiving ICMPv4 packets.
523
524 #### Definition
525 ```python
526 ICMPv4Socket()
527 ```
616 Class for sending and receiving ICMPv4 packets.
617
618 ```python
619 ICMPv4Socket(address=None, privileged=True)
620 ```
621
622 #### Parameters
623 - `source`
624
625 The IP address from which you want to listen and send packets. By default, the socket listens on all interfaces.
626
627 - Type: `str`
628 - Default: `None`
629
630 - `privileged`
631
632 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.
633
634 *Only available on Unix systems. Ignored on Windows.*
635
636 - Type: `bool`
637 - Default: `True`
528638
529639 #### Methods
530 - `__init__()`
640 - `__init__(address=None, privileged=True)`
531641
532642 *Constructor. Automatically called: do not call it directly.*
533643
534 - Raises `SocketPermissionError`: If the permissions are insufficient to create the socket.
644 - Raises `SocketPermissionError`: If the privileges are insufficient to create the socket.
645 - Raises `SocketAddressError`: If the requested address cannot be assigned to the socket.
646 - Raises `ICMPSocketError`: If another error occurs while creating the socket.
535647
536648 - `__del__()`
537649
538650 *Destructor. Automatically called: do not call it directly.*
539651
540 - Call the `close` method.
652 Call the `close` method.
541653
542654 - `send(request)`
543655
544 Send a request to a host.
545
656 Send an ICMP request message over the network to a remote host.<br>
546657 This operation is non-blocking. Use the `receive` method to get the reply.
547658
548 - Parameter `ICMPRequest`: The ICMP request you have created.
659 - 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.
549660 - Raises `SocketBroadcastError`: If a broadcast address is used and the corresponding option is not enabled on the socket (ICMPv4 only).
550661 - Raises `SocketUnavailableError`: If the socket is closed.
551662 - Raises `ICMPSocketError`: If another error occurs while sending.
552663
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.
664 - `receive(request=None, timeout=2)`
665
666 Receive an ICMP reply message from the socket.<br>
667 This method can be called multiple times if you expect several responses as with a broadcast address.
668
669 - Parameter `request` *(ICMPRequest)*: The ICMP request to use to match the response. By default, all ICMP packets arriving on the socket are returned.
670 - Parameter `timeout` *(int or float)*: The maximum waiting time for receiving the response in seconds. Default to `2`.
671 - Raises `TimeoutExceeded`: If no response is received before the timeout specified in parameters.
560672 - Raises `SocketUnavailableError`: If the socket is closed.
561673 - 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.
674
675 Returns an `ICMPReply` object representing the response of the desired destination or an upstream gateway.
563676
564677 - `close()`
565678
566679 Close the socket. It cannot be used after this call.
567680
568 #### Getters only
681 #### Properties
682 - `address`
683
684 The IP address from which the socket listens and sends packets. Return `None` if the socket listens on all interfaces.
685
686 - Type: `str`
687
688 - `is_privileged`
689
690 Indicate whether the socket is running in privileged mode.
691
692 - Type: `bool`
693
569694 - `is_closed`
570695
571696 Indicate whether the socket is closed.
572697
573698 - Type: `bool`
574699
575 #### Getters / Setters
700 #### Properties and setters
576701 - `broadcast`
577702
578703 Enable or disable the broadcast support on the socket.
583708 <br>
584709
585710 ### 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.
711 Class for sending and receiving ICMPv6 packets.
712
713 ```python
714 ICMPv6Socket(address=None, privileged=True)
715 ```
716
717 #### Methods and properties
718 The same methods and properties as for the `ICMPv4Socket` class, except the `broadcast` property.
595719
596720 <br>
597721
600724
601725 ```
602726 ICMPLibError
727 ├─ NameLookupError
603728 ├─ ICMPSocketError
729 │ ├─ SocketAddressError
604730 │ ├─ SocketPermissionError
605731 │ ├─ SocketUnavailableError
606732 │ ├─ SocketBroadcastError
607733 │ └─ TimeoutExceeded
608
734
609735 └─ ICMPError
610736 ├─ DestinationUnreachable
611737 │ ├─ ICMPv4DestinationUnreachable
612738 │ └─ ICMPv6DestinationUnreachable
613
739
614740 └─ TimeExceeded
615741 ├─ ICMPv4TimeExceeded
616742 └─ ICMPv6TimeExceeded
617743 ```
618744
619745 - `ICMPLibError`: Exception class for the icmplib package.
746 - `NameLookupError`: Raised when the requested name does not exist or cannot be resolved. This concerns both Fully Qualified Domain Names and hostnames.
620747 - `ICMPSocketError`: Base class for ICMP sockets exceptions.
621 - `SocketPermissionError`: Raised when the permissions are insufficient to create a socket.
748 - `SocketAddressError`: Raised when the requested address cannot be assigned to the socket.
749 - `SocketPermissionError`: Raised when the privileges are insufficient to create the socket.
622750 - `SocketUnavailableError`: Raised when an action is performed while the socket is closed.
623751 - `SocketBroadcastError`: Raised when a broadcast address is used and the corresponding option is not enabled on the socket.
624752 - `TimeoutExceeded`: Raised when a timeout occurs on a socket.
626754 - `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.
627755 - `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.
628756
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).
757 Use the `message` property to get the error message. `ICMPError` subclasses have a `reply` property to retrieve the response.
632758
633759 <br>
634760
637763 ```python
638764 def single_ping(address, timeout=2, id=PID):
639765 # Create an ICMP socket
640 socket = ICMPv4Socket()
641
642 # Create a request
643 # See the ICMPRequest class for details
766 sock = ICMPv4Socket()
767
768 # Create an ICMP request
769 # See the 'ICMPRequest' class for details
644770 request = ICMPRequest(
645771 destination=address,
646772 id=id,
647 sequence=1,
648 timeout=timeout)
773 sequence=1)
649774
650775 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
776 sock.send(request)
777
778 # If the program arrives in this section, it means that the
779 # packet has been transmitted.
780
781 reply = sock.receive(request, timeout)
782
783 # If the program arrives in this section, it means that a
784 # packet has been received. The reply has the same identifier
785 # and sequence number that the request but it can come from
786 # an intermediate gateway.
662787
663788 reply.raise_for_status()
664789
665 # If the program arrives in this section,
666 # it means that the destination host has responded to
667 # the request
790 # If the program arrives in this section, it means that the
791 # destination host has responded to the request.
668792
669793 except TimeoutExceeded as err:
670794 # The timeout has been reached
671 # Equivalent to print(err.message)
672795 print(err)
673796
674797 except DestinationUnreachable as err:
675 # The reply indicates that the destination host is
676 # unreachable
798 # The reply indicates that the destination host is unreachable
677799 print(err)
678800
679801 # Retrieve the response
680802 reply = err.reply
681803
682804 except TimeExceeded as err:
683 # The reply indicates that the time to live exceeded
684 # in transit
805 # The reply indicates that the time to live exceeded in transit
685806 print(err)
686807
687808 # Retrieve the response
697818 #### Verbose ping
698819 ```python
699820 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
821 # A payload of 56 bytes is used by default. You can modify it using
822 # the 'payload_size' parameter of your ICMP request.
823 print(f'PING {address}: 56 data bytes\n')
824
825 # We detect the socket to use from the specified IP address
705826 if is_ipv6_address(address):
706 socket = ICMPv6Socket()
827 sock = ICMPv6Socket()
707828
708829 else:
709 socket = ICMPv4Socket()
830 sock = ICMPv4Socket()
710831
711832 for sequence in range(count):
712833 # We create an ICMP request
713834 request = ICMPRequest(
714835 destination=address,
715836 id=id,
716 sequence=sequence,
717 timeout=timeout)
837 sequence=sequence)
718838
719839 try:
720840 # We send the request
721 socket.send(request)
841 sock.send(request)
722842
723843 # We are awaiting receipt of an ICMP reply
724 reply = socket.receive()
844 reply = sock.receive(request, timeout)
725845
726846 # We received a reply
727847 # We display some information
767887
768888 ## FAQ
769889
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')
890 ### How to resolve a FQDN/domain name or a hostname?
891 The use of the built-in `resolve` function is recommended:
892
893 ```python
894 >>> resolve('github.com')
775895 '140.82.118.4'
776896 ```
777897
898 - If several IP addresses are available, only the first one is returned. This function searches for IPv4 addresses first before searching for IPv6 addresses.
899 - If you pass an IP address, no lookup is done. The same address is returned.
900 - Raises a `NameLookupError` exception if the requested name does not exist or cannot be resolved.
901
902 ### How to use the library without root privileges?
903 Since its version 2.0, icmplib can be used without root privileges.
904
905 For this, you can set the `privileged` parameter to `False` on the `ping` and `multiping` functions, as well as the low level classes. By disabling this parameter, the kernel handles some parts of the ICMP headers.
906
907 On some Linux systems, you must allow this feature:
908
909 ```shell
910 $ echo 'net.ipv4.ping_group_range = 0 2147483647' | sudo tee -a /etc/sysctl.conf
911 $ sudo sysctl -p
912 ```
913
914 You can check the current value with the following command:
915
916 ```shell
917 $ sysctl net.ipv4.ping_group_range
918 net.ipv4.ping_group_range = 0 2147483647
919 ```
920
921 *Since Ubuntu 20.04 LTS, this manipulation is no longer necessary.*
922
923 [Read more on www.kernel.org](https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt)
924
925 ### Why I have no response from a remote host?
926 In the event of no response from a remote host, several causes are possible:
927 - Your computer's firewall may not be properly configured. This impacts in particular the `traceroute` function which can no longer receive ICMP Time Exceeded messages.
928 - The remote host or an upstream gateway is down.
929 - The remote host or an upstream gateway drops ICMP messages for security reasons.
930 - In the case of the `traceroute` function, if the last host in the list is not the one expected, more than 30 hops (default) may be needed to reach it. You can try increasing the value of the `max_hops` parameter.
931
932 <br>
933
778934 ## Contributing
779935
780936 Comments and enhancements are welcome.
789945
790946 ## License
791947
792 Copyright 2017-2020 Valentin BELYN.
948 Copyright 2017-2021 Valentin BELYN.
793949
794950 Code released under the GNU LGPLv3 license. See the [LICENSE](LICENSE) for details.
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 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
47 https://github.com/ValentinBELYN/icmplib
58
6 :copyright: Copyright 2017-2020 Valentin BELYN.
9 :copyright: Copyright 2017-2021 Valentin BELYN.
710 :license: GNU LGPLv3, see the LICENSE for details.
811
912 ~~~~~~~
1518 64 bytes from 10.0.0.17: icmp_seq=0 time=1.065 ms
1619 64 bytes from 10.0.0.40: icmp_seq=0 time=1.595 ms
1720 64 bytes from 10.0.0.41: icmp_seq=0 time=9.471 ms
18
1921 64 bytes from 10.0.0.17: icmp_seq=1 time=0.983 ms
2022 64 bytes from 10.0.0.40: icmp_seq=1 time=1.579 ms
2123 64 bytes from 10.0.0.41: icmp_seq=1 time=9.345 ms
22
2324 64 bytes from 10.0.0.17: icmp_seq=2 time=0.916 ms
2425 64 bytes from 10.0.0.40: icmp_seq=2 time=2.031 ms
2526 64 bytes from 10.0.0.41: icmp_seq=2 time=9.554 ms
26
2727 64 bytes from 10.0.0.17: icmp_seq=3 time=1.112 ms
2828 64 bytes from 10.0.0.40: icmp_seq=3 time=1.384 ms
2929 64 bytes from 10.0.0.41: icmp_seq=3 time=9.565 ms
30
31 Completed.
3032 '''
3133
32 from icmplib import (
33 ICMPv4Socket,
34 ICMPRequest,
35 TimeoutExceeded,
36 ICMPLibError,
37 PID)
34 from icmplib import ICMPv4Socket, ICMPRequest
35 from icmplib import ICMPLibError, TimeoutExceeded, PID
3836
3937
4038 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')
39 # A payload of 56 bytes is used by default. You can modify it using
40 # the 'payload_size' parameter of your ICMP request.
41 print(f'PING {address}: 56 data bytes\n')
4442
4543 # Broadcast is only possible in IPv4
46 socket = ICMPv4Socket()
44 sock = ICMPv4Socket()
4745
4846 # We allow the socket to send broadcast packets
49 socket.broadcast = True
47 sock.broadcast = True
5048
5149 for sequence in range(count):
5250 # We create an ICMP request
5351 request = ICMPRequest(
5452 destination=address,
5553 id=id,
56 sequence=sequence,
57 timeout=timeout)
58
59 print()
54 sequence=sequence)
6055
6156 try:
6257 # We send the request
63 socket.send(request)
58 sock.send(request)
6459
6560 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()
61 # We are awaiting receipt of an ICMP reply. If there is
62 # no more responses, the 'TimeoutExceeded' exception is
63 # thrown and the loop is stopped.
64 reply = sock.receive(request, timeout)
7065
71 # We calculate the round-trip time of the reply
66 # We calculate the round-trip time
7267 round_trip_time = (reply.time - request.time) * 1000
7368
7469 # We display some information
8378
8479 except ICMPLibError:
8580 # All other errors
86 print('An error has occurred.')
81 print(' An error has occurred.')
82
83 print('\nCompleted.')
8784
8885
8986 # Limited broadcast
11 icmplib
22 ~~~~~~~
33
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
47 https://github.com/ValentinBELYN/icmplib
58
6 :copyright: Copyright 2017-2020 Valentin BELYN.
9 :copyright: Copyright 2017-2021 Valentin BELYN.
710 :license: GNU LGPLv3, see the LICENSE for details.
811
912 ~~~~~~~
1114 Example: multiping
1215 '''
1316
14 from icmplib import multiping
17 from icmplib import resolve, multiping
1518
1619
1720 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
2321 # IPv4 addresses
2422 '1.1.1.1',
2523 '8.8.8.8',
2826
2927 # IPv6 addresses
3028 '::1',
29
30 # Hostnames and Fully Qualified Domain Names (FQDNs) are not
31 # allowed. You can easily retrieve their IP address by calling the
32 # built-in 'resolve' function. The first address returned from the
33 # DNS resolution will be used. For deterministic behavior, prefer
34 # to use an IP address.
35 resolve('github.com')
3136 ]
3237
33 hosts = multiping(addresses, count=2, interval=0.5, timeout=1)
38 hosts = multiping(addresses, count=2, timeout=1)
3439
3540 hosts_alive = []
3641 hosts_dead = []
4348 hosts_dead.append(host.address)
4449
4550 print(hosts_alive)
46 # ['github.com', '1.1.1.1', '8.8.8.8', '::1']
51 # ['1.1.1.1', '8.8.8.8', '::1', '140.82.121.4']
4752
4853 print(hosts_dead)
4954 # ['10.0.0.100', '10.0.0.200']
11 icmplib
22 ~~~~~~~
33
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
47 https://github.com/ValentinBELYN/icmplib
58
6 :copyright: Copyright 2017-2020 Valentin BELYN.
9 :copyright: Copyright 2017-2021 Valentin BELYN.
710 :license: GNU LGPLv3, see the LICENSE for details.
811
912 ~~~~~~~
2023 print(host.address)
2124 # '1.1.1.1'
2225
23 # The minimum round-trip time
26 # The minimum round-trip time in milliseconds
2427 print(host.min_rtt)
2528 # 12.2
2629
27 # The average round-trip time
30 # The average round-trip time in milliseconds
2831 print(host.avg_rtt)
2932 # 13.2
3033
31 # The maximum round-trip time
34 # The maximum round-trip time in milliseconds
3235 print(host.max_rtt)
3336 # 17.6
3437
11 icmplib
22 ~~~~~~~
33
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
47 https://github.com/ValentinBELYN/icmplib
58
6 :copyright: Copyright 2017-2020 Valentin BELYN.
9 :copyright: Copyright 2017-2021 Valentin BELYN.
710 :license: GNU LGPLv3, see the LICENSE for details.
811
912 ~~~~~~~
1417 from icmplib import traceroute
1518
1619
17 hops = traceroute('1.1.1.1', timeout=1, fast_mode=True)
20 hops = traceroute('1.1.1.1', timeout=1, fast=True)
1821
1922 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
23 # [ <Hop 1 [10.0.0.1]>,
24 # <Hop 2 [194.149.169.49]>,
25 # <Hop 3 [194.149.166.54]>,
26 # <Hop 5 [212.73.205.22]>,
27 # <Hop 6 [1.1.1.1]> ]
2528
2629 last_distance = 0
2730
2831 for hop in hops:
2932 if last_distance + 1 != hop.distance:
30 print(' * Some routers are not responding')
33 print(' * Some gateways are not responding')
3134
32 print(f'{hop.distance:4} {hop.address:15} '
35 print(f' {hop.distance:<2} {hop.address:15} '
3336 f'{hop.avg_rtt} ms')
3437
3538 last_distance = hop.distance
3639
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
40 # 1 10.0.0.1 5.196 ms
41 # 2 194.149.169.49 7.552 ms
42 # 3 194.149.166.54 12.21 ms
43 # * Some gateways are not responding
44 # 5 212.73.205.22 22.15 ms
45 # 6 1.1.1.1 13.59 ms
11 icmplib
22 ~~~~~~~
33
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
47 https://github.com/ValentinBELYN/icmplib
58
6 :copyright: Copyright 2017-2020 Valentin BELYN.
9 :copyright: Copyright 2017-2021 Valentin BELYN.
710 :license: GNU LGPLv3, see the LICENSE for details.
811
912 ~~~~~~~
1619 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms
1720 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms
1821 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms
22
23 Completed.
1924 '''
20
21 from icmplib import (
22 ICMPv4Socket,
23 ICMPv6Socket,
24 ICMPRequest,
25 TimeoutExceeded,
26 ICMPError,
27 ICMPLibError,
28 is_ipv6_address,
29 PID)
3025
3126 from time import sleep
3227
28 from icmplib import ICMPv4Socket, ICMPv6Socket, ICMPRequest
29 from icmplib import ICMPLibError, ICMPError, TimeoutExceeded
30 from icmplib import PID, is_ipv6_address
31
3332
3433 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
34 # A payload of 56 bytes is used by default. You can modify it using
35 # the 'payload_size' parameter of your ICMP request.
3736 print(f'PING {address}: 56 data bytes\n')
3837
39 # Detection of the socket to use
38 # We detect the socket to use from the specified IP address
4039 if is_ipv6_address(address):
41 socket = ICMPv6Socket()
40 sock = ICMPv6Socket()
4241
4342 else:
44 socket = ICMPv4Socket()
43 sock = ICMPv4Socket()
4544
4645 for sequence in range(count):
4746 # We create an ICMP request
4847 request = ICMPRequest(
4948 destination=address,
5049 id=id,
51 sequence=sequence,
52 timeout=timeout)
50 sequence=sequence)
5351
5452 try:
5553 # We send the request
56 socket.send(request)
54 sock.send(request)
5755
5856 # We are awaiting receipt of an ICMP reply
59 reply = socket.receive()
57 reply = sock.receive(request, timeout)
6058
6159 # We received a reply
6260 # We display some information
7876
7977 except TimeoutExceeded:
8078 # The timeout has been reached
81 print(f'Request timeout for icmp_seq {sequence}')
79 print(f' Request timeout for icmp_seq {sequence}')
8280
8381 except ICMPError as err:
8482 # An ICMP error message has been received
8684
8785 except ICMPLibError:
8886 # All other errors
89 print('An error has occurred.')
87 print(' An error has occurred.')
88
89 print('\nCompleted.')
9090
9191
9292 verbose_ping('1.1.1.1')
11 icmplib
22 ~~~~~~~
33
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
47 https://github.com/ValentinBELYN/icmplib
58
6 :copyright: Copyright 2017-2020 Valentin BELYN.
9 :copyright: Copyright 2017-2021 Valentin BELYN.
710 :license: GNU LGPLv3, see the LICENSE for details.
811
912 ~~~~~~~
1316 Traceroute to ovh.com (198.27.92.1): 56 data bytes, 30 hops max
1417
1518 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
19 2 194.149.164.56 194.149.164.56 4.61 ms
20 3 213.186.32.181 be100-159.th2-1-a9.fr.eu 11.97 ms
21 4 94.23.122.146 be102.rbx-g1-nc5.fr.eu 15.81 ms
1922 5 * * *
20 6 37.187.231.75 be5.rbx-iplb1-a70.fr.eu 17.1 ms
23 6 37.187.231.75 be5.rbx-iplb1-a70.fr.eu 17.12 ms
2124 7 198.27.92.1 www.ovh.com 10.87 ms
25
26 Completed.
2227 '''
2328
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
29 from socket import getfqdn
3530 from time import sleep
3631
32 from icmplib import ICMPv4Socket, ICMPv6Socket, ICMPRequest
33 from icmplib import ICMPLibError, TimeoutExceeded, TimeExceeded
34 from icmplib import PID, resolve, is_ipv6_address
3735
38 def verbose_traceroute(address, count=3, interval=0.05, timeout=2,
36
37 def verbose_traceroute(address, count=2, interval=0.05, timeout=2,
3938 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)}): '
39 # We perform a DNS resolution of the address passed in parameters.
40 # If the address is already an IP address, no lookup is done. The
41 # same address is returned.
42 ip_address = resolve(address)
43
44 # A payload of 56 bytes is used by default. You can modify it using
45 # the 'payload_size' parameter of your ICMP request.
46 print(f'Traceroute to {address} ({ip_address}): '
4347 f'56 data bytes, {max_hops} hops max\n')
4448
45 # Detection of the socket to use
46 if is_ipv6_address(address):
47 socket = ICMPv6Socket()
49 # We detect the socket to use from the specified IP address
50 if is_ipv6_address(ip_address):
51 sock = ICMPv6Socket()
4852
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 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
47 https://github.com/ValentinBELYN/icmplib
58
6 :copyright: Copyright 2017-2020 Valentin BELYN.
9 :copyright: Copyright 2017-2021 Valentin BELYN.
710 :license: GNU LGPLv3, see the LICENSE for details.
811
912 ~~~~~~~
2831 from .ping import ping, 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 PID, resolve, is_ipv4_address, is_ipv6_address
3235
3336
3437 __author__ = 'Valentin BELYN'
35 __copyright__ = 'Copyright 2017-2020 Valentin BELYN'
38 __copyright__ = 'Copyright 2017-2021 Valentin BELYN'
3639 __license__ = 'GNU Lesser General Public License v3.0'
3740
38 __version__ = '1.2.2'
39 __build__ = '201010'
41 __version__ = '2.1.1'
42 __build__ = '210321'
11 icmplib
22 ~~~~~~~
33
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
47 https://github.com/ValentinBELYN/icmplib
58
6 :copyright: Copyright 2017-2020 Valentin BELYN.
9 :copyright: Copyright 2017-2021 Valentin BELYN.
710 :license: GNU LGPLv3, see the LICENSE for details.
811
912 ~~~~~~~
4043 return self._message
4144
4245
46 class NameLookupError(ICMPLibError):
47 '''
48 Raised when the requested name does not exist or cannot be
49 resolved. This concerns both Fully Qualified Domain Names and
50 hostnames.
51
52 '''
53 def __init__(self, name):
54 message = f'The name \'{name}\' cannot be resolved'
55 super().__init__(message)
56
57
4358 class ICMPSocketError(ICMPLibError):
4459 '''
4560 Base class for ICMP sockets exceptions.
4762 '''
4863
4964
65 class SocketAddressError(ICMPSocketError):
66 '''
67 Raised when the requested address cannot be assigned to the socket.
68
69 '''
70 def __init__(self, address):
71 message = f'The requested address ({address}) cannot be ' \
72 'assigned to the socket'
73 super().__init__(message)
74
75
5076 class SocketPermissionError(ICMPSocketError):
5177 '''
52 Raised when the permissions are insufficient to create a socket.
78 Raised when the privileges are insufficient to create the socket.
5379
5480 '''
5581 def __init__(self):
75101 '''
76102 def __init__(self):
77103 message = 'Broadcast is not allowed: ' \
78 'please use broadcast method (setter) to allow it'
104 'please use the \'broadcast\' property to allow it'
79105 super().__init__(message)
80106
81107
+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 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
47 https://github.com/ValentinBELYN/icmplib
58
6 :copyright: Copyright 2017-2020 Valentin BELYN.
9 :copyright: Copyright 2017-2021 Valentin BELYN.
710 :license: GNU LGPLv3, see the LICENSE for details.
811
912 ~~~~~~~
2427 '''
2528
2629 from .exceptions import *
27 from .utils import is_ipv6_address
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.
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 packet.
6565 Provides a defined level of service to the packet by setting
6666 the DS Field (formerly TOS) or the Traffic Class field of the
6767 IP header. Packets are delivered with the minimum priority by
7171
7272 '''
7373 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
74 payload_size=56, ttl=64, traffic_class=0):
7875
7976 if payload:
8077 payload_size = len(payload)
8178
8279 self._destination = destination
83 self._id = id
84 self._sequence = sequence
80 self._id = id & 0xffff
81 self._sequence = sequence & 0xffff
8582 self._payload = payload
8683 self._payload_size = payload_size
87 self._timeout = timeout
8884 self._ttl = ttl
8985 self._traffic_class = traffic_class
9086 self._time = 0
9591 @property
9692 def destination(self):
9793 '''
98 The IP address of the gateway or host to which the message
99 should be sent.
94 The IP address of the host to which the message should be sent.
10095
10196 '''
10297 return self._destination
137132 return self._payload_size
138133
139134 @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
148135 def ttl(self):
149136 '''
150 The time to live of the packet in seconds.
137 The time to live of the packet in terms of hops.
151138
152139 '''
153140 return self._ttl
164151 def time(self):
165152 '''
166153 The timestamp of the ICMP request.
154
167155 Initialized to zero when creating the request and replaced by
168 `ICMPv4Socket` or `ICMPv6Socket` with the time of sending.
156 the `send` method of `ICMPv4Socket` or `ICMPv6Socket` with the
157 time of sending.
169158
170159 '''
171160 return self._time
173162
174163 class ICMPReply:
175164 '''
176 A class that represents an ICMP reply. Generated from an
177 `ICMPSocket` object (`ICMPv4Socket` or `ICMPv6Socket`).
165 A class that represents an ICMP reply. Generated from an ICMP
166 socket (`ICMPv4Socket` or `ICMPv6Socket`).
178167
179168 :type source: str
180169 :param source: The IP address of the gateway or host that composes
189178 the request.
190179
191180 :type type: int
192 :param type: The type of message.
181 :param type: The type of ICMP message.
193182
194183 :type code: int
195 :param code: The error code.
184 :param code: The ICMP error code.
196185
197186 :type bytes_received: int
198187 :param bytes_received: The number of bytes received.
217206
218207 def raise_for_status(self):
219208 '''
220 Throw an exception if the reply is not an ICMP ECHO_REPLY.
209 Throw an exception if the reply is not an ICMP Echo Reply.
221210 Otherwise, do nothing.
222211
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):
212 :raises DestinationUnreachable: If the destination is
213 unreachable for some reason.
214 :raises TimeExceeded: If the time to live field of the ICMP
215 request has reached zero.
216 :raises ICMPError: Raised for any other type and ICMP error
217 code, except ICMP Echo Reply messages.
218
219 '''
220 if ':' in self._source:
234221 echo_reply_type = 129
222
235223 errors = {
236224 1: ICMPv6DestinationUnreachable,
237225 3: ICMPv6TimeExceeded
239227
240228 else:
241229 echo_reply_type = 0
230
242231 errors = {
243232 3: ICMPv4DestinationUnreachable,
244233 11: ICMPv4TimeExceeded
265254 @property
266255 def id(self):
267256 '''
268 The identifier of the request.
257 The identifier of the reply.
269258 Used to match the reply with the request.
270259
271260 '''
283272 @property
284273 def type(self):
285274 '''
286 The type of message.
275 The type of ICMP message.
287276
288277 '''
289278 return self._type
291280 @property
292281 def code(self):
293282 '''
294 The error code.
283 The ICMP error code.
295284
296285 '''
297286 return self._code
305294 return self._bytes_received
306295
307296 @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
320297 def time(self):
321298 '''
322299 The timestamp of the ICMP reply.
327304
328305 class Host:
329306 '''
330 A class that represents a host. Simplifies the exploitation of
331 results from `ping` and `traceroute` functions.
307 A class that represents a host. It simplifies the use of the
308 results from the `ping`, `multiping` and `traceroute` functions.
332309
333310 :type address: str
334311 :param address: The IP address of the gateway or host that
335312 responded to the request.
336313
337314 :type min_rtt: float
338 :param min_rtt: The minimum round-trip time.
315 :param min_rtt: The minimum round-trip time in milliseconds.
339316
340317 :type avg_rtt: float
341 :param avg_rtt: The average round-trip time.
318 :param avg_rtt: The average round-trip time in milliseconds.
342319
343320 :type max_rtt: float
344 :param max_rtt: The maximum round-trip time.
321 :param max_rtt: The maximum round-trip time in milliseconds.
345322
346323 :type packets_sent: int
347324 :param packets_sent: The number of packets transmitted to the
377354 @property
378355 def min_rtt(self):
379356 '''
380 The minimum round-trip time.
357 The minimum round-trip time in milliseconds.
381358
382359 '''
383360 return self._min_rtt
385362 @property
386363 def avg_rtt(self):
387364 '''
388 The average round-trip time.
365 The average round-trip time in milliseconds.
389366
390367 '''
391368 return self._avg_rtt
393370 @property
394371 def max_rtt(self):
395372 '''
396 The maximum round-trip time.
373 The maximum round-trip time in milliseconds.
397374
398375 '''
399376 return self._max_rtt
404381 The number of packets transmitted to the destination host.
405382
406383 '''
407 return self._packets_sent
408
409 @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
419384 return self._packets_sent
420385
421386 @property
428393 return self._packets_received
429394
430395 @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
441
442 @property
443396 def packet_loss(self):
444397 '''
445398 Packet loss occurs when packets fail to reach their destination.
449402 if not self._packets_sent:
450403 return 0.0
451404
452 return 1 - self._packets_received / self._packets_sent
405 packet_loss = 1 - self._packets_received / self._packets_sent
406
407 return round(packet_loss, 3)
453408
454409 @property
455410 def is_alive(self):
456411 '''
457 Indicate whether the host is reachable. Return a `boolean`.
412 Indicate whether the host is reachable.
413 Return a `boolean`.
458414
459415 '''
460416 return self._packets_received > 0
470426 responded to the request.
471427
472428 :type min_rtt: float
473 :param min_rtt: The minimum round-trip time.
429 :param min_rtt: The minimum round-trip time in milliseconds.
474430
475431 :type avg_rtt: float
476 :param avg_rtt: The average round-trip time.
432 :param avg_rtt: The average round-trip time in milliseconds.
477433
478434 :type max_rtt: float
479 :param max_rtt: The maximum round-trip time.
435 :param max_rtt: The maximum round-trip time in milliseconds.
480436
481437 :type packets_sent: int
482438 :param packets_sent: The number of packets transmitted to the
487443 host and received by the current host.
488444
489445 :type distance: int
490 :param distance: The distance (in terms of hops) that separates the
446 :param distance: The distance, in terms of hops, that separates the
491447 remote host from the current machine.
492448
493449 '''
505461 @property
506462 def distance(self):
507463 '''
508 The distance (in terms of hops) that separates the remote host
464 The distance, in terms of hops, that separates the remote host
509465 from the current machine.
510466
511467 '''
11 icmplib
22 ~~~~~~~
33
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
47 https://github.com/ValentinBELYN/icmplib
58
6 :copyright: Copyright 2017-2020 Valentin BELYN.
9 :copyright: Copyright 2017-2021 Valentin BELYN.
710 :license: GNU LGPLv3, see the LICENSE for details.
811
912 ~~~~~~~
2326 <https://www.gnu.org/licenses/>.
2427 '''
2528
26 from threading import Thread
2729 from time import sleep
2830
29 from .sockets import ICMPv4Socket, ICMPv6Socket
31 from .sockets import ICMPv4Socket, ICMPv6Socket, BufferedSocket
3032 from .models import ICMPRequest, Host
3133 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):
34 from .utils import PID, PLATFORM_LINUX, resolve, is_ipv6_address
35
36
37 def ping(address, count=4, interval=1, timeout=2, id=PID, source=None,
38 privileged=True, **kwargs):
5039 '''
51 Send ICMP ECHO_REQUEST packets to a network host.
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
52 packet. 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
61 should be used for every ping process. On Linux, this
62 identifier is ignored when the `privileged` parameter is
63 disabled.
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 privileged: bool, optional
71 :param privileged: When this option is enabled, this library fully
72 manages the exchanges and the structure of ICMP packets.
73 Disable this option if you want to use this function without
74 root privileges and let the kernel handle ICMP headers.
75 Default to True.
76 Only available on Unix systems. Ignored on Windows.
77
78 Advanced (**kwags):
79
80 :type payload: bytes, optional
81 :param payload: The payload content in bytes. A random payload is
82 used by default.
83
84 :type payload_size: int, optional
85 :param payload_size: The payload size. Ignored when the `payload`
86 parameter is set. Default to 56.
87
88 :type traffic_class: int, optional
89 :param traffic_class: The traffic class of ICMP packets.
90 Provides a defined level of service to packets by setting the
91 DS Field (formerly TOS) or the Traffic Class field of IP
92 headers. Packets are delivered with the minimum priority by
93 default (Best-effort delivery).
94 Intermediate routers must be able to support this feature.
95 Only available on Unix systems. Ignored on Windows.
7596
7697 :rtype: Host
7798 :returns: A `Host` object containing statistics about the desired
7899 destination.
79100
80 :raises SocketPermissionError: If the permissions are insufficient
81 to create a socket.
101 :raises NameLookupError: If you pass a hostname or FQDN in
102 parameters and it does not exist or cannot be resolved.
103 :raises SocketPermissionError: If the privileges are insufficient
104 to create the socket.
105 :raises SocketAddressError: If the source address cannot be
106 assigned to the socket.
107 :raises ICMPSocketError: If another error occurs. See the
108 `ICMPv4Socket` or `ICMPv6Socket` class for details.
82109
83110 Usage::
84111
85112 >>> from icmplib import ping
86113 >>> host = ping('1.1.1.1')
87
88114 >>> host.avg_rtt
89115 13.2
90
91116 >>> host.is_alive
92117 True
93118
97122 address = resolve(address)
98123
99124 if is_ipv6_address(address):
100 socket = ICMPv6Socket()
125 sock = ICMPv6Socket(
126 address=source,
127 privileged=privileged)
101128
102129 else:
103 socket = ICMPv4Socket()
130 sock = ICMPv4Socket(
131 address=source,
132 privileged=privileged)
104133
105134 packets_sent = 0
106135 packets_received = 0
114143 destination=address,
115144 id=id,
116145 sequence=sequence,
117 timeout=timeout,
118146 **kwargs)
119147
120148 try:
121 socket.send(request)
149 sock.send(request)
122150 packets_sent += 1
123151
124 reply = socket.receive()
152 reply = sock.receive(request, timeout)
125153 reply.raise_for_status()
126154 packets_received += 1
127155
150178 packets_sent=packets_sent,
151179 packets_received=packets_received)
152180
153 socket.close()
181 sock.close()
154182
155183 return host
156184
157185
158 def multiping(addresses, count=2, interval=1, timeout=2, id=PID,
159 max_threads=10, **kwargs):
186 def multiping(addresses, count=2, interval=0.01, timeout=2, id=PID,
187 source=None, privileged=True, **kwargs):
160188 '''
161 Send ICMP ECHO_REQUEST packets to multiple network hosts.
189 Send ICMP Echo Request packets to several network hosts.
190
191 This function relies on a single thread to send multiple packets
192 simultaneously. If you mix IPv4 and IPv6 addresses, up to two
193 threads are used.
162194
163195 :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.
196 :param addresses: The IP addresses of the hosts to which messages
197 should be sent. Hostnames and FQDNs are not allowed. You can
198 easily retrieve their IP address by calling the built-in
199 `resolve` function.
200
201 :type count: int, optional
202 :param count: The number of ping to perform per address.
203 Default to 2.
204
205 :type interval: int or float, optional
206 :param interval: The interval in seconds between sending each
207 packet. Default to 0.01.
208
209 :type timeout: int or float, optional
210 :param timeout: The maximum waiting time for receiving all
211 responses in seconds. Default to 2.
212
213 :type id: int, optional
214 :param id: The identifier of ICMP requests. Used to match the
215 responses with requests. This identifier will be incremented by
216 one for each destination. On Linux, this identifier is ignored
217 when the `privileged` parameter is disabled.
218
219 :type source: str, optional
220 :param source: The IP address from which you want to send packets.
221 By default, the interface is automatically chosen according to
222 the specified destinations. This parameter should not be used
223 if you are passing both IPv4 and IPv6 addresses to this
224 function.
225
226 :type privileged: bool, optional
227 :param privileged: When this option is enabled, this library fully
228 manages the exchanges and the structure of ICMP packets.
229 Disable this option if you want to use this function without
230 root privileges and let the kernel handle ICMP headers.
231 Default to True.
232 Only available on Unix systems. Ignored on Windows.
233
234 Advanced (**kwags):
235
236 :type payload: bytes, optional
237 :param payload: The payload content in bytes. A random payload is
238 used by default.
239
240 :type payload_size: int, optional
241 :param payload_size: The payload size. Ignored when the `payload`
242 parameter is set. Default to 56.
243
244 :type traffic_class: int, optional
245 :param traffic_class: The traffic class of ICMP packets.
246 Provides a defined level of service to packets by setting the
247 DS Field (formerly TOS) or the Traffic Class field of IP
248 headers. Packets are delivered with the minimum priority by
249 default (Best-effort delivery).
250 Intermediate routers must be able to support this feature.
251 Only available on Unix systems. Ignored on Windows.
188252
189253 :rtype: list of Host
190254 :returns: A list of `Host` objects containing statistics about the
191255 desired destinations. The list is sorted in the same order as
192256 the addresses passed in parameters.
193257
194 :raises SocketPermissionError: If the permissions are insufficient
195 to create a socket.
258 :raises SocketPermissionError: If the privileges are insufficient
259 to create the socket.
260 :raises SocketAddressError: If the source address cannot be
261 assigned to the socket.
262 :raises ICMPSocketError: If another error occurs. See the
263 `ICMPv4Socket` or `ICMPv6Socket` class for details.
196264
197265 Usage::
198266
213281 See the `Host` class for details.
214282
215283 '''
284 index = {}
285 sock_ipv4 = None
286 sock_ipv6 = None
287 sequence_offset = 0
288
289 # We create the ICMP requests and instantiate the sockets
290 for i, address in enumerate(addresses):
291 if not privileged and PLATFORM_LINUX:
292 sequence_offset = i * count
293
294 requests = [
295 ICMPRequest(
296 destination=address,
297 id=id + i,
298 sequence=sequence + sequence_offset,
299 **kwargs)
300
301 for sequence in range(count)
302 ]
303
304 if is_ipv6_address(address):
305 if not sock_ipv6:
306 sock_ipv6 = BufferedSocket(
307 ICMPv6Socket(
308 address=source,
309 privileged=privileged))
310
311 sock = sock_ipv6
312
313 else:
314 if not sock_ipv4:
315 sock_ipv4 = BufferedSocket(
316 ICMPv4Socket(
317 address=source,
318 privileged=privileged))
319
320 sock = sock_ipv4
321
322 index[address] = requests, sock
323
324 # We send the ICMP requests
325 for sequence in range(count):
326 for address in addresses:
327 request = index[address][0][sequence]
328 sock = index[address][1]
329
330 try:
331 sock.send(request)
332 sleep(interval)
333
334 except ICMPSocketError:
335 pass
336
216337 hosts = []
217 inactive_threads = []
218 active_threads = []
219
220 for i, address in enumerate(addresses):
221 thread = PingThread(
338
339 # We retrieve the responses and relate them to the ICMP requests
340 for address in addresses:
341 requests = index[address][0]
342 sock = index[address][1]
343
344 packets_received = 0
345 min_rtt = float('inf')
346 avg_rtt = 0.0
347 max_rtt = 0.0
348
349 for request in requests:
350 try:
351 reply = sock.receive(request, timeout)
352 reply.raise_for_status()
353 packets_received += 1
354
355 round_trip_time = (reply.time - request.time) * 1000
356 avg_rtt += round_trip_time
357 min_rtt = min(round_trip_time, min_rtt)
358 max_rtt = max(round_trip_time, max_rtt)
359
360 except ICMPLibError:
361 pass
362
363 if packets_received:
364 avg_rtt /= packets_received
365
366 else:
367 min_rtt = 0.0
368
369 host = Host(
222370 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)
371 min_rtt=min_rtt,
372 avg_rtt=avg_rtt,
373 max_rtt=max_rtt,
374 packets_sent=len(requests),
375 packets_received=packets_received)
376
377 hosts.append(host)
378
379 if sock_ipv4:
380 sock_ipv4.close()
381
382 if sock_ipv6:
383 sock_ipv6.close()
245384
246385 return hosts
11 icmplib
22 ~~~~~~~
33
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
47 https://github.com/ValentinBELYN/icmplib
58
6 :copyright: Copyright 2017-2020 Valentin BELYN.
9 :copyright: Copyright 2017-2021 Valentin BELYN.
710 :license: GNU LGPLv3, see the LICENSE for details.
811
912 ~~~~~~~
2427 '''
2528
2629 import socket
30
31 from threading import Thread, Lock, Event
2732 from struct import pack, unpack
2833 from time import time
2934
30 from .ip import IPv4Socket, IPv6Socket
3135 from .models import ICMPRequest, ICMPReply
3236 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
37 from .utils import *
11638
11739
11840 class ICMPSocket:
11941 '''
12042 Base class for ICMP sockets.
12143
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
44 :type address: str, optional
45 :param address: The IP address from which you want to listen and
46 send packets. By default, the socket listens on all interfaces.
47
48 :type privileged: bool, optional
49 :param privileged: When this option is enabled, the socket fully
50 manages the exchanges and the structure of the ICMP packets.
51 Disable this option if you want to instantiate and use the
52 socket without root privileges and let the kernel handle ICMP
53 headers. Default to True.
54 Only available on Unix systems. Ignored on Windows.
55
56 :raises SocketPermissionError: If the privileges are insufficient
12757 to create the socket.
58 :raises SocketAddressError: If the requested address cannot be
59 assigned to the socket.
60 :raises ICMPSocketError: If another error occurs while creating the
61 socket.
12862
12963 '''
130 def __init__(self, config):
131 self._socket = None
132 self._config = config
133 self._last_request = None
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
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
193 reply cannot be parsed.
194
195 '''
196 bytes_received = len(packet) - self._ICMP_HEADER_OFFSET
197
198 if len(packet) < self._ICMP_CHECKSUM_OFFSET:
202199 return None
203200
204201 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:
202 self._ICMP_HEADER_OFFSET:
203 self._ICMP_CHECKSUM_OFFSET])
204
205 if type != self._ICMP_ECHO_REPLY:
206 packet = packet[
207 self._ICMP_PAYLOAD_OFFSET
208 - self._ICMP_HEADER_OFFSET
209 + self._ICMP_HEADER_REAL_OFFSET:]
210
211 if len(packet) < self._ICMP_PAYLOAD_OFFSET:
212212 return None
213213
214214 id, sequence = unpack('!2H', packet[
215 self._config.ICMP_ID_OFFSET:
216 self._config.ICMP_PAYLOAD_OFFSET
217 ])
218
219 reply = ICMPReply(
215 self._ICMP_ID_OFFSET:
216 self._ICMP_PAYLOAD_OFFSET])
217
218 return ICMPReply(
220219 source=source,
221220 id=id,
222221 sequence=sequence,
223222 type=type,
224223 code=code,
225224 bytes_received=bytes_received,
226 time=reply_time)
227
228 return reply
225 time=current_time)
229226
230227 def send(self, request):
231228 '''
232 Send a request to a host.
229 Send an ICMP request message over the network to a remote host.
233230
234231 This operation is non-blocking. Use the `receive` method to get
235232 the reply.
236233
237234 :type request: ICMPRequest
238 :param request: The ICMP request you have created.
235 :param request: The ICMP request you have created. If the
236 socket is used in non-privileged mode on a Linux system,
237 the identifier defined in the request will be replaced by
238 the kernel.
239239
240240 :raises SocketBroadcastError: If a broadcast address is used
241241 and the corresponding option is not enabled on the socket
244244 :raises ICMPSocketError: If another error occurs while sending.
245245
246246 '''
247 if not self._socket:
247 if not self._sock:
248248 raise SocketUnavailableError
249249
250 payload = request.payload
251
252 if not payload:
253 payload = random_byte_message(
254 size=request.payload_size)
250 payload = request.payload or \
251 random_byte_message(request.payload_size)
255252
256253 packet = self._create_packet(
257254 id=request.id,
258255 sequence=request.sequence,
259256 payload=payload)
260257
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 self._set_ttl(request.ttl)
260 self._set_traffic_class(request.traffic_class)
261
262 request._time = time()
263 self._sock.sendto(packet, (request.destination, 0))
264
265 # On Linux, the ICMP request identifier is replaced by the
266 # kernel with a random port number when a datagram socket
267 # is used (SOCK_DGRAM). So, we update the request created
268 # by the user to take this new identifier into account.
269 if not self._privileged and PLATFORM_LINUX:
270 request._id = self._sock.getsockname()[1]
276271
277272 except PermissionError:
278273 raise SocketBroadcastError
280275 except OSError as err:
281276 raise ICMPSocketError(str(err))
282277
283 def receive(self):
284 '''
285 Receive a reply from a host.
278 def receive(self, request=None, timeout=2):
279 '''
280 Receive an ICMP reply message from the socket.
286281
287282 This method can be called multiple times if you expect several
288 responses (as with a broadcast address).
283 responses as with a broadcast address.
284
285 :type request: ICMPRequest, optional
286 :param request: The ICMP request to use to match the response.
287 By default, all ICMP packets arriving on the socket are
288 returned.
289
290 :type timeout: int or float, optional
291 :param timeout: The maximum waiting time for receiving the
292 response in seconds. Default to 2.
293
294 :rtype: ICMPReply
295 :returns: An `ICMPReply` object representing the response of
296 the desired destination or an upstream gateway. See the
297 `ICMPReply` class for details.
289298
290299 :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.
300 timeout specified in parameters.
294301 :raises SocketUnavailableError: If the socket is closed.
295302 :raises ICMPSocketError: If another error occurs while
296303 receiving.
297304
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:
305 '''
306 if not self._sock:
306307 raise SocketUnavailableError
307308
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
309 self._sock.settimeout(timeout)
310 time_limit = time() + timeout
316311
317312 try:
318313 while True:
319 packet, address, port = self._socket.receive()
320 reply_time = time()
321
322 if reply_time > timeout:
314 response = self._sock.recvfrom(1024)
315 current_time = time()
316
317 packet = response[0]
318 source = response[1][0]
319
320 if current_time > time_limit:
323321 raise socket.timeout
324322
325 reply = self._read_reply(
323 # On Linux, the IP header is missing when a datagram
324 # socket is used (SOCK_DGRAM). To keep the same
325 # behavior on all operating systems including macOS
326 # which has this header, we add a padding of the size
327 # of the missing IP header.
328 if not self._privileged and PLATFORM_LINUX:
329 padding = b'\x00' * self._ICMP_HEADER_OFFSET
330 packet = padding + packet
331
332 reply = self._parse_reply(
326333 packet=packet,
327 source=address,
328 reply_time=reply_time)
329
330 if (reply and request.id == reply.id and
334 source=source,
335 current_time=current_time)
336
337 if (reply and not request or
338 reply and request.id == reply.id and
331339 request.sequence == reply.sequence):
332340 return reply
333341
334342 except socket.timeout:
335 raise TimeoutExceeded(request.timeout)
343 raise TimeoutExceeded(timeout)
336344
337345 except OSError as err:
338346 raise ICMPSocketError(str(err))
342350 Close the socket. It cannot be used after this call.
343351
344352 '''
345 if self._socket:
346 self._socket.close()
347 self._socket = None
353 if self._sock:
354 self._sock.close()
355 self._sock = None
356
357 @property
358 def address(self):
359 '''
360 The IP address from which the socket listens and sends packets.
361 Return `None` if the socket listens on all interfaces.
362
363 '''
364 return self._address
365
366 @property
367 def is_privileged(self):
368 '''
369 Indicate whether the socket is running in privileged mode.
370 Return a `boolean`.
371
372 '''
373 return self._privileged
348374
349375 @property
350376 def is_closed(self):
351377 '''
352 Indicate whether the socket is closed. Return a `boolean`.
353
354 '''
355 return self._socket is None
378 Indicate whether the socket is closed.
379 Return a `boolean`.
380
381 '''
382 return self._sock is None
383
384
385 # Echo Request and Echo Reply messages RFC 792
386 #
387 # 0 1 2 3
388 # 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
389 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
390 # | Type | Code | Checksum |
391 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
392 # | Identifier | Sequence Number |
393 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
394 # | Data ...
395 # +-+-+-+-+-
396 #
397 # ICMPv4 Error message RFC 792
398 #
399 # 0 1 2 3
400 # 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
401 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
402 # | Type | Code | Checksum |
403 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
404 # | Unused / Depends on the error |
405 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
406 # | Internet Header + 64 bits of Original Data Datagram |
407 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
356408
357409
358410 class ICMPv4Socket(ICMPSocket):
359411 '''
360 Socket for sending and receiving ICMPv4 packets.
361
362 :raises SocketPermissionError: If the permissions are insufficient
412 Class for sending and receiving ICMPv4 packets.
413
414 :type address: str, optional
415 :param address: The IP address from which you want to listen and
416 send packets. By default, the socket listens on all interfaces.
417
418 :type privileged: bool, optional
419 :param privileged: When this option is enabled, the socket fully
420 manages the exchanges and the structure of the ICMP packets.
421 Disable this option if you want to instantiate and use the
422 socket without root privileges and let the kernel handle ICMP
423 headers. Default to True.
424 Only available on Unix systems. Ignored on Windows.
425
426 :raises SocketPermissionError: If the privileges are insufficient
363427 to create the socket.
428 :raises SocketAddressError: If the requested address cannot be
429 assigned to the socket.
430 :raises ICMPSocketError: If another error occurs while creating the
431 socket.
364432
365433 '''
366 def __init__(self):
367 super().__init__(ICMPv4Config)
434 _ICMP_HEADER_OFFSET = 20
435 _ICMP_HEADER_REAL_OFFSET = 20
436
437 _ICMP_CODE_OFFSET = _ICMP_HEADER_OFFSET + 1
438 _ICMP_CHECKSUM_OFFSET = _ICMP_HEADER_OFFSET + 2
439 _ICMP_ID_OFFSET = _ICMP_HEADER_OFFSET + 4
440 _ICMP_SEQUENCE_OFFSET = _ICMP_HEADER_OFFSET + 6
441 _ICMP_PAYLOAD_OFFSET = _ICMP_HEADER_OFFSET + 8
442
443 _ICMP_ECHO_REQUEST = 8
444 _ICMP_ECHO_REPLY = 0
445
446 def _create_socket(self, type):
447 '''
448 Create and return a new socket.
449
450 '''
451 return socket.socket(
452 family=socket.AF_INET,
453 type=type,
454 proto=socket.IPPROTO_ICMP)
455
456 def _set_ttl(self, ttl):
457 '''
458 Set the time to live of every IP packet originating from this
459 socket.
460
461 '''
462 self._sock.setsockopt(
463 socket.IPPROTO_IP,
464 socket.IP_TTL,
465 ttl)
466
467 def _set_traffic_class(self, traffic_class):
468 '''
469 Set the DS Field (formerly TOS) of every IP packet originating
470 from this socket.
471
472 Only available on Unix systems. Ignored on Windows.
473
474 '''
475 if PLATFORM_WINDOWS:
476 return
477
478 self._sock.setsockopt(
479 socket.IPPROTO_IP,
480 socket.IP_TOS,
481 traffic_class)
368482
369483 @property
370484 def broadcast(self):
377491 icmp_socket.broadcast = True
378492
379493 '''
380 return self._socket.broadcast
494 return self._sock.getsockopt(
495 socket.SOL_SOCKET,
496 socket.SO_BROADCAST) > 0
381497
382498 @broadcast.setter
383499 def broadcast(self, allow):
384 self._socket.broadcast = allow
500 self._sock.setsockopt(
501 socket.SOL_SOCKET,
502 socket.SO_BROADCAST,
503 allow)
504
505
506 # Echo Request and Echo Reply messages RFC 4443
507 #
508 # 0 1 2 3
509 # 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
510 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
511 # | Type | Code | Checksum |
512 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
513 # | Identifier | Sequence Number |
514 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
515 # | Data ...
516 # +-+-+-+-+-
517 #
518 # ICMPv6 Error message RFC 4443
519 #
520 # 0 1 2 3
521 # 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
522 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
523 # | Type | Code | Checksum |
524 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
525 # | Unused / Depends on the error |
526 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
527 # | Original packet without exceed the minimum IPv6 MTU |
528 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
529
530 # Windows IPv6 compatibility
531 if PLATFORM_WINDOWS: socket.IPPROTO_IPV6 = 41
385532
386533
387534 class ICMPv6Socket(ICMPSocket):
388535 '''
389 Socket for sending and receiving ICMPv6 packets.
390
391 :raises SocketPermissionError: If the permissions are insufficient
536 Class for sending and receiving ICMPv6 packets.
537
538 :type address: str, optional
539 :param address: The IP address from which you want to listen and
540 send packets. By default, the socket listens on all interfaces.
541
542 :type privileged: bool, optional
543 :param privileged: When this option is enabled, the socket fully
544 manages the exchanges and the structure of the ICMP packets.
545 Disable this option if you want to instantiate and use the
546 socket without root privileges and let the kernel handle ICMP
547 headers. Default to True.
548 Only available on Unix systems. Ignored on Windows.
549
550 :raises SocketPermissionError: If the privileges are insufficient
392551 to create the socket.
552 :raises SocketAddressError: If the requested address cannot be
553 assigned to the socket.
554 :raises ICMPSocketError: If another error occurs while creating the
555 socket.
393556
394557 '''
395 def __init__(self):
396 super().__init__(ICMPv6Config)
558 _ICMP_HEADER_OFFSET = 0
559 _ICMP_HEADER_REAL_OFFSET = 40
560
561 _ICMP_CODE_OFFSET = _ICMP_HEADER_OFFSET + 1
562 _ICMP_CHECKSUM_OFFSET = _ICMP_HEADER_OFFSET + 2
563 _ICMP_ID_OFFSET = _ICMP_HEADER_OFFSET + 4
564 _ICMP_SEQUENCE_OFFSET = _ICMP_HEADER_OFFSET + 6
565 _ICMP_PAYLOAD_OFFSET = _ICMP_HEADER_OFFSET + 8
566
567 _ICMP_ECHO_REQUEST = 128
568 _ICMP_ECHO_REPLY = 129
569
570 def _create_socket(self, type):
571 '''
572 Create and return a new socket.
573
574 '''
575 return socket.socket(
576 family=socket.AF_INET6,
577 type=type,
578 proto=socket.IPPROTO_ICMPV6)
579
580 def _set_ttl(self, ttl):
581 '''
582 Set the time to live of every IP packet originating from this
583 socket.
584
585 '''
586 self._sock.setsockopt(
587 socket.IPPROTO_IPV6,
588 socket.IPV6_UNICAST_HOPS,
589 ttl)
590
591 def _set_traffic_class(self, traffic_class):
592 '''
593 Set the Traffic Class field of every IP packet originating from
594 this socket.
595
596 Only available on Unix systems. Ignored on Windows.
597
598 '''
599 if PLATFORM_WINDOWS:
600 return
601
602 self._sock.setsockopt(
603 socket.IPPROTO_IPV6,
604 socket.IPV6_TCLASS,
605 traffic_class)
606
607
608 class BufferedSocket:
609 '''
610 A wrapper for ICMP sockets that reads and classifies incoming ICMP
611 packets into a buffer, in real time.
612
613 Useful if you want to send several ICMP packets consecutively
614 without waiting for a response between each sending. For this, an
615 internal thread is used. It stops automatically when you call the
616 `detach` or `close` method.
617
618 This feature is experimental. There is no guarantee that it will be
619 retained in future versions.
620
621 :type sock: ICMPSocket
622 :param sock: An ICMP socket. Once the wrapper instantiated, this
623 socket should no longer be used directly.
624
625 '''
626 def __init__(self, sock):
627 self._sock = sock
628 self._buffer = {}
629
630 self._buffer_event = Event()
631 self._buffer_lock = Lock()
632
633 self._thread = Thread(target=self._read_from_socket)
634 self._thread.start()
635
636 def __enter__(self):
637 '''
638 Return this object.
639
640 '''
641 return self
642
643 def __exit__(self, type, value, traceback):
644 '''
645 Call the `close` method.
646
647 '''
648 self.close()
649
650 def __del__(self):
651 '''
652 Call the `close` method.
653
654 '''
655 self.close()
656
657 def _read_from_socket(self):
658 '''
659 Internal thread which retrieves incoming ICMP packets from the
660 socket and adds them to a buffer.
661
662 '''
663 while self._sock:
664 try:
665 reply = self._sock.receive(timeout=1)
666
667 with self._buffer_lock:
668 buffer_id = reply.id, reply.sequence
669
670 if buffer_id not in self._buffer:
671 self._buffer[buffer_id] = []
672
673 self._buffer[buffer_id].append(reply)
674 self._buffer_event.set()
675
676 except ICMPSocketError:
677 pass
678
679 def send(self, request):
680 '''
681 Send an ICMP request message over the network to a remote host.
682
683 This operation is non-blocking. Use the `receive` method to get
684 the reply.
685
686 :type request: ICMPRequest
687 :param request: The ICMP request you have created. If the
688 underlying socket is used in non-privileged mode on a Linux
689 system, the identifier defined in the request will be
690 replaced by the kernel.
691
692 :raises SocketBroadcastError: If a broadcast address is used
693 and the corresponding option is not enabled on the
694 underlying socket (ICMPv4 only).
695 :raises SocketUnavailableError: If the socket is closed.
696 :raises ICMPSocketError: If another error occurs while sending.
697
698 '''
699 if not self._sock:
700 raise SocketUnavailableError
701
702 self._sock.send(request)
703
704 def receive(self, request, timeout=2):
705 '''
706 Retrieve an ICMP reply message from the buffer.
707
708 This method can be called multiple times if you expect several
709 responses as with a broadcast address.
710
711 :type request: ICMPRequest
712 :param request: The ICMP request to use to match the response.
713
714 :type timeout: int or float, optional
715 :param timeout: The maximum waiting time for receiving the
716 response in seconds. This parameter takes into account the
717 timestamp of the request. Default to 2.
718
719 :rtype: ICMPReply
720 :returns: An `ICMPReply` object representing the response of
721 the desired destination or an upstream gateway. See the
722 `ICMPReply` class for details.
723
724 :raises TimeoutExceeded: If no response is received before the
725 timeout specified in parameters.
726
727 '''
728 buffer_id = request.id, request.sequence
729
730 while True:
731 with self._buffer_lock:
732 self._buffer_event.clear()
733
734 if buffer_id in self._buffer:
735 reply = self._buffer[buffer_id].pop(0)
736
737 if not self._buffer[buffer_id]:
738 del self._buffer[buffer_id]
739
740 return reply
741
742 remaining_time = request.time + timeout - time()
743
744 if not self._buffer_event.wait(remaining_time):
745 raise TimeoutExceeded(timeout)
746
747 def detach(self):
748 '''
749 Detach the socket from the wrapper and return it. The wrapper
750 cannot be used after this call but the socket can be reused for
751 other purposes.
752
753 '''
754 sock = self._sock
755
756 if self._sock:
757 self._sock = None
758 self._thread.join()
759
760 return sock
761
762 def close(self):
763 '''
764 Close this object and the underlying socket. Both cannot be
765 used after this call.
766
767 '''
768 if self._sock:
769 self.detach().close()
770
771 @property
772 def is_closed(self):
773 '''
774 Indicate whether the wrapper is still operational or not.
775 Return a `boolean`.
776
777 '''
778 return self._sock is None
11 icmplib
22 ~~~~~~~
33
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
47 https://github.com/ValentinBELYN/icmplib
58
6 :copyright: Copyright 2017-2020 Valentin BELYN.
9 :copyright: Copyright 2017-2021 Valentin BELYN.
710 :license: GNU LGPLv3, see the LICENSE for details.
811
912 ~~~~~~~
3134 from .utils import PID, resolve, is_ipv6_address
3235
3336
34 def traceroute(address, count=3, interval=0.05, timeout=2, id=PID,
35 traffic_class=0, max_hops=30, fast_mode=False, **kwargs):
37 def traceroute(address, count=2, interval=0.05, timeout=2, id=PID,
38 first_hop=1, max_hops=30, source=None, fast=False, **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.
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
59 packet. 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 id: int, optional
66 :param id: The identifier of ICMP requests. Used to match the
67 responses with requests. In practice, a unique identifier
68 should be used for every traceroute process. By default, the
69 identifier corresponds to the PID.
70
71 :type first_hop: int, optional
72 :param first_hop: The initial time to live value used in outgoing
73 probe packets. Default to 1.
74
75 :type max_hops: int, optional
76 :param max_hops: The maximum time to live (max number of hops) used
77 in outgoing probe packets. Default to 30.
78
79 :type source: str, optional
80 :param source: The IP address from which you want to send packets.
81 By default, the interface is automatically chosen according to
82 the specified destination.
83
84 :type fast: bool, optional
85 :param fast: When this option is enabled and an intermediate router
86 has been reached, skip to the next hop rather than perform
87 additional requests. The `count` parameter then becomes the
88 maximum number of requests in the event of no response.
89 Default to False.
90
91 Advanced (**kwags):
92
93 :type payload: bytes, optional
94 :param payload: The payload content in bytes. A random payload is
95 used by default.
96
97 :type payload_size: int, optional
98 :param payload_size: The payload size. Ignored when the `payload`
99 parameter is set. Default to 56.
100
101 :type traffic_class: int, optional
102 :param traffic_class: The traffic class of ICMP packets.
66103 Provides a defined level of service to packets by setting the
67104 DS Field (formerly TOS) or the Traffic Class field of IP
68105 headers. Packets are delivered with the minimum priority by
70107 Intermediate routers must be able to support this feature.
71108 Only available on Unix systems. Ignored on Windows.
72109
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
87110 :rtype: list of Hop
88111 :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.
112 desired destination. The list is sorted in ascending order
113 according to the distance, in terms of hops, that separates the
114 remote host from the current machine. Gateways that do not
115 respond to requests are not added to this list.
116
117 :raises NameLookupError: If you pass a hostname or FQDN in
118 parameters and it does not exist or cannot be resolved.
119 :raises SocketPermissionError: If the privileges are insufficient
120 to create the socket.
121 :raises SocketAddressError: If the source address cannot be
122 assigned to the socket.
123 :raises ICMPSocketError: If another error occurs. See the
124 `ICMPv4Socket` or `ICMPv6Socket` class for details.
95125
96126 Usage::
97127
101131
102132 >>> for hop in hops:
103133 ... if last_distance + 1 != hop.distance:
104 ... print('Some routers are not responding')
134 ... print('Some gateways are not responding')
105135 ...
106136 ... print(f'{hop.distance} {hop.address} {hop.avg_rtt} ms')
107137 ...
108138 ... last_distance = hop.distance
109139 ...
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
140 1 10.0.0.1 5.196 ms
141 2 194.149.169.49 7.552 ms
142 3 194.149.166.54 12.21 ms
143 * Some gateways are not responding
144 5 212.73.205.22 22.15 ms
145 6 1.1.1.1 13.59 ms
116146
117147 See the `Hop` class for details.
118148
120150 address = resolve(address)
121151
122152 if is_ipv6_address(address):
123 socket = ICMPv6Socket()
153 sock = ICMPv6Socket(source)
124154
125155 else:
126 socket = ICMPv4Socket()
127
128 ttl = 1
156 sock = ICMPv4Socket(source)
157
158 ttl = first_hop
129159 host_reached = False
130160 hops = []
131161
143173 destination=address,
144174 id=id,
145175 sequence=sequence,
146 timeout=timeout,
147176 ttl=ttl,
148 traffic_class=traffic_class,
149177 **kwargs)
150178
151179 try:
152 socket.send(request)
180 sock.send(request)
153181 packets_sent += 1
154182
155 reply = socket.receive()
183 reply = sock.receive(request, timeout)
156184 reply.raise_for_status()
157185 host_reached = True
158186
170198 min_rtt = min(round_trip_time, min_rtt)
171199 max_rtt = max(round_trip_time, max_rtt)
172200
173 if fast_mode:
201 if fast:
174202 break
175203
176204 if packets_received:
189217
190218 ttl += 1
191219
192 socket.close()
220 sock.close()
193221
194222 return hops
11 icmplib
22 ~~~~~~~
33
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
47 https://github.com/ValentinBELYN/icmplib
58
6 :copyright: Copyright 2017-2020 Valentin BELYN.
9 :copyright: Copyright 2017-2021 Valentin BELYN.
710 :license: GNU LGPLv3, see the LICENSE for details.
811
912 ~~~~~~~
2427 '''
2528
2629 import socket
30
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'
3543
3644
3947 Generate a random byte sequence of the specified size.
4048
4149 '''
42 bytes_available = (
50 sequence = choices(
4351 b'abcdefghijklmnopqrstuvwxyz'
4452 b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
45 b'1234567890'
46 )
53 b'1234567890', k=size)
4754
48 return bytes(
49 choices(bytes_available, k=size)
50 )
55 return bytes(sequence)
5156
5257
53 def resolve(name):
58 def resolve(name, family=None):
5459 '''
5560 Resolve a hostname or FQDN into an IP address. If several IP
5661 addresses are available, only the first one is returned.
5762
58 This function searches for IPv4 addresses first for compatibility
59 reasons before searching for IPv6 addresses.
63 :type name: str
64 :param name: A hostname or a Fully Qualified Domain Name (FQDN).
65 If you pass an IP address, no lookup is done. The same address
66 is returned.
6067
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.
68 :type family: int, optional
69 :param family: The address family. Can be set to `4` for IPv4 or
70 `6` for IPv6 addresses. By default, this function searches for
71 IPv4 addresses first for compatibility reasons (A DNS lookup)
72 before searching for IPv6 addresses (AAAA DNS lookup).
73
74 :raises NameLookupError: If the requested name does not exist or
75 cannot be resolved.
6476
6577 '''
6678 if is_ipv4_address(name) or is_ipv6_address(name):
6779 return name
6880
69 try:
70 return socket.getaddrinfo(
71 host=name,
72 port=None,
73 family=socket.AF_INET,
74 type=socket.SOCK_DGRAM
75 )[0][4][0]
81 if family is None or family == 4:
82 try:
83 return socket.getaddrinfo(
84 host=name,
85 port=None,
86 family=socket.AF_INET,
87 type=socket.SOCK_DGRAM
88 )[0][4][0]
7689
77 except OSError:
78 pass
90 except OSError:
91 pass
7992
80 try:
81 return socket.getaddrinfo(
82 host=name,
83 port=None,
84 family=socket.AF_INET6,
85 type=socket.SOCK_DGRAM
86 )[0][4][0]
93 if family is None or family == 6:
94 try:
95 return socket.getaddrinfo(
96 host=name,
97 port=None,
98 family=socket.AF_INET6,
99 type=socket.SOCK_DGRAM
100 )[0][4][0]
87101
88 except OSError:
89 return name
102 except OSError:
103 pass
104
105 raise NameLookupError(name)
90106
91107
92108 def is_ipv4_address(address):
95111 Return a `boolean`.
96112
97113 '''
98 return match(
99 r'^([0-9]{1,3}[.]){3}[0-9]{1,3}$',
100 address
101 ) is not None
114 pattern = r'^([0-9]{1,3}[.]){3}[0-9]{1,3}$'
115 return match(pattern, address) is not None
102116
103117
104118 def is_ipv6_address(address):
Binary diff not shown
00 [metadata]
11 name = icmplib
2 version = 1.2.2
2 version = 2.1.1
33 description = Easily forge ICMP packets and make your own ping and traceroute.
44 keywords = pure, implementation, icmp, sockets, ping, multiping, traceroute, ipv4, ipv6, python3
55
11 icmplib
22 ~~~~~~~
33
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
47 https://github.com/ValentinBELYN/icmplib
58
6 :copyright: Copyright 2017-2020 Valentin BELYN.
9 :copyright: Copyright 2017-2021 Valentin BELYN.
710 :license: GNU LGPLv3, see the LICENSE for details.
811
912 ~~~~~~~