Codebase list python-icmplib / dc71f16
Import upstream version 2.1.1+git20210515.1.6d8ca5c Kali Janitor 3 years ago
26 changed file(s) with 4008 addition(s) and 1794 deletion(s). Raw diff Collapse all Expand all
+0
-61
CHANGELOG.md less more
0 # Changelog
1
2 All notable changes to this project will be documented in this file.
3
4 ## [v1.2.2](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.2.2) - 2020-10-10
5 - Add support for hostnames and FQDN resolution to IPv6 addresses.
6 - Performance improvement.
7
8 ## [v1.2.1](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.2.1) - 2020-09-26
9 - Fix an issue in the `traceroute` function which gave the wrong value for the `avg_rtt` property.
10 - Some other tweaks to the `traceroute` function.
11
12 ## [v1.2.0](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.2.0) - 2020-09-12
13 - Add the ability to modify the traffic class of ICMP packets.
14 - Add new optional parameters to the `traceroute` function.
15 - Add a new exception `SocketUnavailableError` when an action is performed while a socket is closed.
16 - Add a warning message on deprecated properties.
17 - Explicit closure of sockets on built-in functions.
18 - Fix a bug when ICMP responses are not correctly formatted (part 2).
19
20 ## [v1.1.3](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.1.3) - 2020-09-03
21 - Fix a bug when ICMP responses are not correctly formatted.
22
23 ## [v1.1.2](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.1.2) - 2020-08-29
24 - Fix a compatibility issue.
25
26 ## [v1.1.1](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.1.1) - 2020-07-10
27 - Fix a bug when the source host does not have an IP address.
28
29 ## [v1.1.0](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.1.0) - 2020-06-25
30 - Normalize the names of variables and properties:
31 - `ICMPReply` class: the `received_bytes` property is deprecated. Use `bytes_received` instead.
32 - `Host` and `Hop` classes: the `transmitted_packets` property is deprecated. Use `packets_sent` instead.
33 - `Host` and `Hop` classes: the `received_packets` property is deprecated. Use `packets_received` instead.
34 - Normalize docstrings.
35 - Add support for odd size payloads.
36 - Optimizations.
37
38 ## [v1.0.4](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.0.4) - 2020-06-14
39 - Add the `is_closed` property to the `ICMPSocket` class.
40 - Round round-trip time values by default.
41 - Fix a bug in the `multiping` function: the `id` parameter was ignored.
42 - Fix a bug in the `ICMPSocket` class when instantiated without root privileges.
43 - Add an index for examples.
44
45 ## [v1.0.3](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.0.3) - 2020-05-09
46 - Add the ability to customize the payload.
47 - Improvements of `ping` and `multiping` functions. It is now possible to pass arguments to the `ICMPRequest` object using keywords arguments `**kwargs`.
48 - Update some docstrings.
49 - Add new examples.
50
51 ## [v1.0.2](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.0.2) - 2019-10-20
52 - Change the license. This project now uses the more permissive license LGPLv3.
53
54 ## [v1.0.1](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.0.1) - 2019-10-07
55 - Add some examples.
56 - Rename `model.py` to `models.py`.
57 - Update setup keywords.
58
59 ## [v1.0.0](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.0.0) - 2019-09-29
60 - :tada: Initial release.
0 Metadata-Version: 2.1
1 Name: icmplib
2 Version: 3.0
3 Summary: Easily forge ICMP packets and make your own ping and traceroute.
4 Home-page: https://github.com/ValentinBELYN/icmplib
5 Author: Valentin BELYN
6 Author-email: [email protected]
7 License: GNU Lesser General Public License v3.0
8 Description: <div align="center">
9 <br>
10 <img src="media/icmplib-logo-2.0.png" height="190" width="100" alt="icmplib">
11 <br>
12 <br>
13
14 <br>
15 <div>
16 <a href="#features">Features</a>&nbsp;&nbsp;&nbsp;
17 <a href="#installation">Installation</a>&nbsp;&nbsp;&nbsp;
18 <a href="#built-in-functions">Built-in functions</a>&nbsp;&nbsp;&nbsp;
19 <a href="#icmp-sockets">ICMP sockets</a>&nbsp;&nbsp;&nbsp;
20 <a href="#faq">FAQ</a>&nbsp;&nbsp;&nbsp;
21 <a href="#contributing">Contributing</a>&nbsp;&nbsp;&nbsp;
22 <a href="#donate">Donate</a>&nbsp;&nbsp;&nbsp;
23 <a href="#license">License</a>
24 </div>
25 <br>
26
27 <pre>icmplib is a brand new and modern implementation of the ICMP protocol in Python.
28 Use the built-in functions or build your own, you have the choice!
29
30 <a href="#how-to-use-the-library-without-root-privileges">- You can now use this library without root privileges -</a></pre>
31 <a href="https://pypi.org/project/icmplib">
32 <img src="https://img.shields.io/pypi/dm/icmplib.svg?style=flat-square&labelColor=0366d6&color=005cc5" alt="statistics">
33 </a>
34 </div>
35
36 <br>
37
38 ## Features
39
40 - :deciduous_tree: **Ready-to-use:** icmplib offers ready-to-use functions such as the most popular ones: `ping`, `multiping` and `traceroute`.
41 - :gem: **Modern:** This library uses the latest technologies offered by Python 3.6+ and is fully object-oriented.
42 - :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!
43 - :zap: **Powerful:** Use the library without root privileges, set the traffic class of ICMP packets, customize their payload and more!
44 - :nut_and_bolt: **Evolutive:** Easily build your own classes and functions with `ICMPv4` and `ICMPv6` sockets.
45 - :fire: **Seamless integration of IPv6:** Use IPv6 the same way you use IPv4. Automatic detection is done without impacting performance.
46 - :rainbow: **Broadcast support** (you must use the `ICMPv4Socket` class to enable it).
47 - :beer: **Support of all operating systems.** Tested on Linux, macOS and Windows.
48 - :metal: **No dependency:** icmplib is a pure Python implementation of the ICMP protocol. It does not use any external dependencies.
49
50 <br>
51
52 ## Installation
53
54 The recommended way to install or upgrade icmplib is to use `pip3`:
55
56 ```shell
57 $ pip3 install icmplib
58 $ pip3 install --upgrade icmplib
59 ```
60
61 *icmplib requires Python 3.6 or later.*
62
63 To import icmplib into your project (only import what you need):
64
65 ```python
66 # For simple use
67 from icmplib import ping, multiping, traceroute, resolve, Host, Hop
68
69 # For advanced use (sockets)
70 from icmplib import ICMPv4Socket, ICMPv6Socket, ICMPRequest, ICMPReply
71
72 # Exceptions
73 from icmplib import ICMPLibError, NameLookupError, ICMPSocketError
74 from icmplib import SocketAddressError, SocketPermissionError
75 from icmplib import SocketUnavailableError, SocketBroadcastError, TimeoutExceeded
76 from icmplib import ICMPError, DestinationUnreachable, TimeExceeded
77 ```
78
79 <br>
80
81 ## Built-in functions
82
83 ### Ping
84 Send ICMP Echo Request packets to a network host.
85
86 ```python
87 ping(address, count=4, interval=1, timeout=2, id=PID, source=None, privileged=True, **kwargs)
88 ```
89
90 #### Parameters
91 - `address`
92
93 The IP address, hostname or FQDN of the host to which messages should be sent. For deterministic behavior, prefer to use an IP address.
94
95 - Type: `str`
96
97 - `count`
98
99 The number of ping to perform.
100
101 - Type: `int`
102 - Default: `4`
103
104 - `interval`
105
106 The interval in seconds between sending each packet.
107
108 - Type: `int` or `float`
109 - Default: `1`
110
111 - `timeout`
112
113 The maximum waiting time for receiving a reply in seconds.
114
115 - Type: `int` or `float`
116 - Default: `2`
117
118 - `id`
119
120 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.
121
122 - Type: `int`
123 - Default: `PID`
124
125 - `source`
126
127 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination.
128
129 - Type: `str`
130 - Default: `None`
131
132 - `privileged`
133
134 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.
135
136 *Only available on Unix systems. Ignored on Windows.*
137
138 - Type: `bool`
139 - Default: `True`
140
141 - `payload`
142
143 The payload content in bytes. A random payload is used by default.
144
145 - Type: `bytes`
146 - Default: `None`
147
148 - `payload_size`
149
150 The payload size. Ignored when the `payload` parameter is set.
151
152 - Type: `int`
153 - Default: `56`
154
155 - `traffic_class`
156
157 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.
158
159 *Only available on Unix systems. Ignored on Windows.*
160
161 - Type: `int`
162 - Default: `0`
163
164 #### Return value
165 - A `Host` object containing statistics about the desired destination:<br>
166 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`, `packets_received`, `packet_loss`, `is_alive`
167
168 #### Exceptions
169 - `NameLookupError`
170
171 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
172
173 - `SocketPermissionError`
174
175 If the privileges are insufficient to create the socket.
176
177 - `SocketAddressError`
178
179 If the source address cannot be assigned to the socket.
180
181 - `ICMPSocketError`
182
183 If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details.
184
185 #### Example
186 ```python
187 >>> host = ping('1.1.1.1', count=10, interval=0.2)
188
189 >>> host.address # The IP address of the host that responded
190 '1.1.1.1' # to the request
191
192 >>> host.min_rtt # The minimum round-trip time
193 12.2
194
195 >>> host.avg_rtt # The average round-trip time
196 13.2
197
198 >>> host.max_rtt # The maximum round-trip time
199 17.6
200
201 >>> host.packets_sent # The number of packets transmitted to the
202 10 # destination host
203
204 >>> host.packets_received # The number of packets sent by the remote
205 9 # host and received by the current host
206
207 >>> host.packet_loss # Packet loss occurs when packets fail to
208 0.1 # reach their destination. Returns a float
209 # between 0 and 1 (all packets are lost)
210 >>> host.is_alive # Indicates whether the host is reachable
211 True
212 ```
213
214 <br>
215
216 ### Multiping
217 Send ICMP Echo Request packets to several network hosts.
218
219 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.
220
221 ```python
222 multiping(addresses, count=2, interval=0.01, timeout=2, id=PID, source=None, privileged=True, **kwargs)
223 ```
224
225 #### Parameters
226 - `addresses`
227
228 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.
229
230 - Type: `list of str`
231
232 - `count`
233
234 The number of ping to perform per address.
235
236 - Type: `int`
237 - Default: `2`
238
239 - `interval`
240
241 The interval in seconds between sending each packet.
242
243 - Type: `int` or `float`
244 - Default: `0.01`
245
246 - `timeout`
247
248 The maximum waiting time for receiving all responses in seconds.
249
250 - Type: `int` or `float`
251 - Default: `2`
252
253 - `id`
254
255 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.
256
257 - Type: `int`
258 - Default: `PID`
259
260 - `source`
261
262 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.
263
264 - Type: `str`
265 - Default: `None`
266
267 - `privileged`
268
269 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.
270
271 *Only available on Unix systems. Ignored on Windows.*
272
273 - Type: `bool`
274 - Default: `True`
275
276 - `payload`
277
278 The payload content in bytes. A random payload is used by default.
279
280 - Type: `bytes`
281 - Default: `None`
282
283 - `payload_size`
284
285 The payload size. Ignored when the `payload` parameter is set.
286
287 - Type: `int`
288 - Default: `56`
289
290 - `traffic_class`
291
292 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.
293
294 *Only available on Unix systems. Ignored on Windows.*
295
296 - Type: `int`
297 - Default: `0`
298
299 #### Return value
300 - A `list of Host` objects containing statistics about the desired destinations:<br>
301 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`, `packets_received`, `packet_loss`, `is_alive`
302
303 The list is sorted in the same order as the addresses passed in parameters.
304
305 #### Exceptions
306 - `SocketPermissionError`
307
308 If the privileges are insufficient to create the socket.
309
310 - `SocketAddressError`
311
312 If the source address cannot be assigned to the socket.
313
314 - `ICMPSocketError`
315
316 If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details.
317
318 #### Example
319 ```python
320 >>> hosts = multiping(['10.0.0.5', '127.0.0.1', '::1'])
321
322 >>> for host in hosts:
323 ... if host.is_alive:
324 ... # See the Host class for details
325 ... print(f'{host.address} is alive!')
326 ...
327 ... else:
328 ... print(f'{host.address} is dead!')
329 ...
330
331 # 10.0.0.5 is dead!
332 # 127.0.0.1 is alive!
333 # ::1 is alive!
334 ```
335
336 <br>
337
338 ### Traceroute
339 Determine the route to a destination host.
340
341 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.
342
343 *This function requires root privileges to run.*
344
345 ```python
346 traceroute(address, count=2, interval=0.05, timeout=2, id=PID, first_hop=1, max_hops=30, source=None, fast=False, **kwargs)
347 ```
348
349 #### Parameters
350 - `address`
351
352 The IP address, hostname or FQDN of the host to reach. For deterministic behavior, prefer to use an IP address.
353
354 - Type: `str`
355
356 - `count`
357
358 The number of ping to perform per hop.
359
360 - Type: `int`
361 - Default: `2`
362
363 - `interval`
364
365 The interval in seconds between sending each packet.
366
367 - Type: `int` or `float`
368 - Default: `0.05`
369
370 - `timeout`
371
372 The maximum waiting time for receiving a reply in seconds.
373
374 - Type: `int` or `float`
375 - Default: `2`
376
377 - `id`
378
379 The identifier of ICMP requests. Used to match the responses with requests. In practice, a unique identifier should be used for every traceroute process.
380
381 - Type: `int`
382 - Default: `PID`
383
384 - `first_hop`
385
386 The initial time to live value used in outgoing probe packets.
387
388 - Type: `int`
389 - Default: `1`
390
391 - `max_hops`
392
393 The maximum time to live (max number of hops) used in outgoing probe packets.
394
395 - Type: `int`
396 - Default: `30`
397
398 - `source`
399
400 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination.
401
402 - Type: `str`
403 - Default: `None`
404
405 - `fast`
406
407 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.
408
409 - Type: `bool`
410 - Default: `False`
411
412 - `payload`
413
414 The payload content in bytes. A random payload is used by default.
415
416 - Type: `bytes`
417 - Default: `None`
418
419 - `payload_size`
420
421 The payload size. Ignored when the `payload` parameter is set.
422
423 - Type: `int`
424 - Default: `56`
425
426 - `traffic_class`
427
428 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.
429
430 *Only available on Unix systems. Ignored on Windows.*
431
432 - Type: `int`
433 - Default: `0`
434
435 #### Return value
436 - 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`.
437
438 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.
439
440 #### Exceptions
441 - `NameLookupError`
442
443 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
444
445 - `SocketPermissionError`
446
447 If the privileges are insufficient to create the socket.
448
449 - `SocketAddressError`
450
451 If the source address cannot be assigned to the socket.
452
453 - `ICMPSocketError`
454
455 If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details.
456
457 #### Example
458 ```python
459 >>> hops = traceroute('1.1.1.1')
460
461 >>> print('Distance/TTL Address Average round-trip time')
462 >>> last_distance = 0
463
464 >>> for hop in hops:
465 ... if last_distance + 1 != hop.distance:
466 ... print('Some gateways are not responding')
467 ...
468 ... # See the Hop class for details
469 ... print(f'{hop.distance} {hop.address} {hop.avg_rtt} ms')
470 ...
471 ... last_distance = hop.distance
472 ...
473
474 # Distance/TTL Address Average round-trip time
475 # 1 10.0.0.1 5.196 ms
476 # 2 194.149.169.49 7.552 ms
477 # 3 194.149.166.54 12.21 ms
478 # * Some gateways are not responding
479 # 5 212.73.205.22 22.15 ms
480 # 6 1.1.1.1 13.59 ms
481 ```
482
483 <br>
484
485 ## ICMP sockets
486
487 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.
488
489 ```
490 ┌──────────────────┐
491 ┌─────────────────┐ send(...) │ ICMPv4Socket │ receive() ┌─────────────────┐
492 │ ICMPRequest │ ────────────> │ or │ ────────────> │ ICMPReply │
493 └─────────────────┘ │ ICMPv6Socket │ └─────────────────┘
494 └──────────────────┘
495 ```
496
497 ### ICMPRequest
498 A user-created object that represents an ICMP Echo Request.
499
500 ```python
501 ICMPRequest(destination, id, sequence, payload=None, payload_size=56, ttl=64, traffic_class=0)
502 ```
503
504 #### Parameters and properties
505 - `destination`
506
507 The IP address of the host to which the message should be sent.
508
509 - Type: `str`
510
511 - `id`
512
513 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.
514
515 - Type: `int`
516
517 - `sequence`
518
519 The sequence number. Used to match the reply with the request. Typically, the sequence number is incremented for each packet sent during the process.
520
521 - Type: `int`
522
523 - `payload`
524
525 The payload content in bytes. A random payload is used by default.
526
527 - Type: `bytes`
528 - Default: `None`
529
530 - `payload_size`
531
532 The payload size. Ignored when the `payload` parameter is set.
533
534 - Type: `int`
535 - Default: `56`
536
537 - `ttl`
538
539 The time to live of the packet in terms of hops.
540
541 - Type: `int`
542 - Default: `64`
543
544 - `traffic_class`
545
546 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.
547
548 *Only available on Unix systems. Ignored on Windows.*
549
550 - Type: `int`
551 - Default: `0`
552
553 #### Properties only
554 - `time`
555
556 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.
557
558 - Type: `float`
559
560 <br>
561
562 ### ICMPReply
563 A class that represents an ICMP reply. Generated from an ICMP socket (`ICMPv4Socket` or `ICMPv6Socket`).
564
565 ```python
566 ICMPReply(source, id, sequence, type, code, bytes_received, time)
567 ```
568
569 #### Parameters and properties
570 - `source`
571
572 The IP address of the gateway or host that composes the ICMP message.
573
574 - Type: `str`
575
576 - `id`
577
578 The identifier of the reply. Used to match the reply with the request.
579
580 - Type: `int`
581
582 - `sequence`
583
584 The sequence number. Used to match the reply with the request.
585
586 - Type: `int`
587
588 - `type`
589
590 The type of ICMP message.
591
592 - Type: `int`
593
594 - `code`
595
596 The ICMP error code.
597
598 - Type: `int`
599
600 - `bytes_received`
601
602 The number of bytes received.
603
604 - Type: `int`
605
606 - `time`
607
608 The timestamp of the ICMP reply.
609
610 - Type: `float`
611
612 #### Methods
613 - `raise_for_status()`
614
615 Throw an exception if the reply is not an ICMP Echo Reply. Otherwise, do nothing.
616
617 - Raises `DestinationUnreachable`: If the destination is unreachable for some reason.
618 - Raises `TimeExceeded`: If the time to live field of the ICMP request has reached zero.
619 - Raises `ICMPError`: Raised for any other type and ICMP error code, except ICMP Echo Reply messages.
620
621 <br>
622
623 ### ICMPv4Socket
624 Class for sending and receiving ICMPv4 packets.
625
626 ```python
627 ICMPv4Socket(address=None, privileged=True)
628 ```
629
630 #### Parameters
631 - `source`
632
633 The IP address from which you want to listen and send packets. By default, the socket listens on all interfaces.
634
635 - Type: `str`
636 - Default: `None`
637
638 - `privileged`
639
640 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.
641
642 *Only available on Unix systems. Ignored on Windows.*
643
644 - Type: `bool`
645 - Default: `True`
646
647 #### Methods
648 - `__init__(address=None, privileged=True)`
649
650 *Constructor. Automatically called: do not call it directly.*
651
652 - Raises `SocketPermissionError`: If the privileges are insufficient to create the socket.
653 - Raises `SocketAddressError`: If the requested address cannot be assigned to the socket.
654 - Raises `ICMPSocketError`: If another error occurs while creating the socket.
655
656 - `__del__()`
657
658 *Destructor. Automatically called: do not call it directly.*
659
660 Call the `close` method.
661
662 - `send(request)`
663
664 Send an ICMP request message over the network to a remote host.<br>
665 This operation is non-blocking. Use the `receive` method to get the reply.
666
667 - 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.
668 - Raises `SocketBroadcastError`: If a broadcast address is used and the corresponding option is not enabled on the socket (ICMPv4 only).
669 - Raises `SocketUnavailableError`: If the socket is closed.
670 - Raises `ICMPSocketError`: If another error occurs while sending.
671
672 - `receive(request=None, timeout=2)`
673
674 Receive an ICMP reply message from the socket.<br>
675 This method can be called multiple times if you expect several responses as with a broadcast address.
676
677 - Parameter `request` *(ICMPRequest)*: The ICMP request to use to match the response. By default, all ICMP packets arriving on the socket are returned.
678 - Parameter `timeout` *(int or float)*: The maximum waiting time for receiving the response in seconds. Default to `2`.
679 - Raises `TimeoutExceeded`: If no response is received before the timeout specified in parameters.
680 - Raises `SocketUnavailableError`: If the socket is closed.
681 - Raises `ICMPSocketError`: If another error occurs while receiving.
682
683 Returns an `ICMPReply` object representing the response of the desired destination or an upstream gateway.
684
685 - `close()`
686
687 Close the socket. It cannot be used after this call.
688
689 #### Properties
690 - `address`
691
692 The IP address from which the socket listens and sends packets. Return `None` if the socket listens on all interfaces.
693
694 - Type: `str`
695
696 - `is_privileged`
697
698 Indicate whether the socket is running in privileged mode.
699
700 - Type: `bool`
701
702 - `is_closed`
703
704 Indicate whether the socket is closed.
705
706 - Type: `bool`
707
708 #### Properties and setters
709 - `broadcast`
710
711 Enable or disable the broadcast support on the socket.
712
713 - Type: `bool`
714 - Default: `False`
715
716 <br>
717
718 ### ICMPv6Socket
719 Class for sending and receiving ICMPv6 packets.
720
721 ```python
722 ICMPv6Socket(address=None, privileged=True)
723 ```
724
725 #### Methods and properties
726 The same methods and properties as for the `ICMPv4Socket` class, except the `broadcast` property.
727
728 <br>
729
730 ### Exceptions
731 The library contains many exceptions to adapt to your needs:
732
733 ```
734 ICMPLibError
735 ├─ NameLookupError
736 ├─ ICMPSocketError
737 │ ├─ SocketAddressError
738 │ ├─ SocketPermissionError
739 │ ├─ SocketUnavailableError
740 │ ├─ SocketBroadcastError
741 │ └─ TimeoutExceeded
742
743 └─ ICMPError
744 ├─ DestinationUnreachable
745 │ ├─ ICMPv4DestinationUnreachable
746 │ └─ ICMPv6DestinationUnreachable
747
748 └─ TimeExceeded
749 ├─ ICMPv4TimeExceeded
750 └─ ICMPv6TimeExceeded
751 ```
752
753 - `ICMPLibError`: Exception class for the icmplib package.
754 - `NameLookupError`: Raised when the requested name does not exist or cannot be resolved. This concerns both Fully Qualified Domain Names and hostnames.
755 - `ICMPSocketError`: Base class for ICMP sockets exceptions.
756 - `SocketAddressError`: Raised when the requested address cannot be assigned to the socket.
757 - `SocketPermissionError`: Raised when the privileges are insufficient to create the socket.
758 - `SocketUnavailableError`: Raised when an action is performed while the socket is closed.
759 - `SocketBroadcastError`: Raised when a broadcast address is used and the corresponding option is not enabled on the socket.
760 - `TimeoutExceeded`: Raised when a timeout occurs on a socket.
761 - `ICMPError`: Base class for ICMP error messages.
762 - `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.
763 - `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.
764
765 Use the `message` property to get the error message. `ICMPError` subclasses have a `reply` property to retrieve the response.
766
767 <br>
768
769 ### Examples
770 #### Sockets in action
771 ```python
772 def single_ping(address, timeout=2, id=PID):
773 # Create an ICMP socket
774 sock = ICMPv4Socket()
775
776 # Create an ICMP request
777 # See the 'ICMPRequest' class for details
778 request = ICMPRequest(
779 destination=address,
780 id=id,
781 sequence=1)
782
783 try:
784 sock.send(request)
785
786 # If the program arrives in this section, it means that the
787 # packet has been transmitted.
788
789 reply = sock.receive(request, timeout)
790
791 # If the program arrives in this section, it means that a
792 # packet has been received. The reply has the same identifier
793 # and sequence number that the request but it can come from
794 # an intermediate gateway.
795
796 reply.raise_for_status()
797
798 # If the program arrives in this section, it means that the
799 # destination host has responded to the request.
800
801 except TimeoutExceeded as err:
802 # The timeout has been reached
803 print(err)
804
805 except DestinationUnreachable as err:
806 # The reply indicates that the destination host is unreachable
807 print(err)
808
809 # Retrieve the response
810 reply = err.reply
811
812 except TimeExceeded as err:
813 # The reply indicates that the time to live exceeded in transit
814 print(err)
815
816 # Retrieve the response
817 reply = err.reply
818
819 except ICMPLibError as err:
820 # All other errors
821 print(err)
822
823 # Automatic socket closure (garbage collector)
824 ```
825
826 #### Verbose ping
827 ```python
828 def verbose_ping(address, count=4, interval=1, timeout=2, id=PID):
829 # A payload of 56 bytes is used by default. You can modify it using
830 # the 'payload_size' parameter of your ICMP request.
831 print(f'PING {address}: 56 data bytes\n')
832
833 # We detect the socket to use from the specified IP address
834 if is_ipv6_address(address):
835 sock = ICMPv6Socket()
836
837 else:
838 sock = ICMPv4Socket()
839
840 for sequence in range(count):
841 # We create an ICMP request
842 request = ICMPRequest(
843 destination=address,
844 id=id,
845 sequence=sequence)
846
847 try:
848 # We send the request
849 sock.send(request)
850
851 # We are awaiting receipt of an ICMP reply
852 reply = sock.receive(request, timeout)
853
854 # We received a reply
855 # We display some information
856 print(f'{reply.bytes_received} bytes from '
857 f'{reply.source}: ', end='')
858
859 # We throw an exception if it is an ICMP error message
860 reply.raise_for_status()
861
862 # We calculate the round-trip time and we display it
863 round_trip_time = (reply.time - request.time) * 1000
864
865 print(f'icmp_seq={sequence} '
866 f'time={round(round_trip_time, 3)} ms')
867
868 # We pause before continuing
869 if sequence < count - 1:
870 sleep(interval)
871
872 except TimeoutExceeded:
873 # The timeout has been reached
874 print(f'Request timeout for icmp_seq {sequence}')
875
876 except ICMPError as err:
877 # An ICMP error message has been received
878 print(err)
879
880 except ICMPLibError:
881 # All other errors
882 print('An error has occurred.')
883
884
885 verbose_ping('1.1.1.1')
886
887 # PING 1.1.1.1: 56 data bytes
888 # 64 bytes from 1.1.1.1: icmp_seq=0 time=12.061 ms
889 # 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms
890 # 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms
891 # 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms
892 ```
893
894 <br>
895
896 ## FAQ
897
898 ### How to resolve a FQDN/domain name or a hostname?
899 The use of the built-in `resolve` function is recommended:
900
901 ```python
902 >>> resolve('github.com')
903 '140.82.118.4'
904 ```
905
906 - If several IP addresses are available, only the first one is returned. This function searches for IPv4 addresses first before searching for IPv6 addresses.
907 - If you pass an IP address, no lookup is done. The same address is returned.
908 - Raises a `NameLookupError` exception if the requested name does not exist or cannot be resolved.
909
910 ### How to use the library without root privileges?
911 Since its version 2.0, icmplib can be used without root privileges.
912
913 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.
914
915 On some Linux systems, you must allow this feature:
916
917 ```shell
918 $ echo 'net.ipv4.ping_group_range = 0 2147483647' | sudo tee -a /etc/sysctl.conf
919 $ sudo sysctl -p
920 ```
921
922 You can check the current value with the following command:
923
924 ```shell
925 $ sysctl net.ipv4.ping_group_range
926 net.ipv4.ping_group_range = 0 2147483647
927 ```
928
929 *Since Ubuntu 20.04 LTS, this manipulation is no longer necessary.*
930
931 [Read more on www.kernel.org](https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt)
932
933 ### Why I have no response from a remote host?
934 In the event of no response from a remote host, several causes are possible:
935 - 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.
936 - The remote host or an upstream gateway is down.
937 - The remote host or an upstream gateway drops ICMP messages for security reasons.
938 - 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.
939
940 <br>
941
942 ## Contributing
943
944 Comments and enhancements are welcome.
945
946 All development is done on [GitHub](https://github.com/ValentinBELYN/icmplib). Use [Issues](https://github.com/ValentinBELYN/icmplib/issues) to report problems and submit feature requests. Please include a minimal example that reproduces the bug.
947
948 ## Donate
949
950 icmplib is completely free and open source. It has been fully developed on my free time. If you enjoy it, please consider donating to support the development.
951
952 - [:tada: Donate via PayPal](https://paypal.me/ValentinBELYN)
953
954 ## License
955
956 Copyright 2017-2021 Valentin BELYN.
957
958 Code released under the GNU LGPLv3 license. See the [LICENSE](LICENSE) for details.
959
960 Keywords: pure,implementation,icmp,sockets,ping,multiping,traceroute,ipv4,ipv6,python3
961 Platform: UNKNOWN
962 Classifier: Development Status :: 5 - Production/Stable
963 Classifier: Intended Audience :: Developers
964 Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
965 Classifier: Operating System :: OS Independent
966 Classifier: Programming Language :: Python
967 Classifier: Programming Language :: Python :: 3
968 Classifier: Programming Language :: Python :: 3 :: Only
969 Classifier: Programming Language :: Python :: 3.7
970 Classifier: Programming Language :: Python :: 3.8
971 Classifier: Programming Language :: Python :: 3.9
972 Classifier: Programming Language :: Python :: 3.10
973 Classifier: Topic :: Software Development :: Libraries
974 Classifier: Topic :: Software Development :: Libraries :: Python Modules
975 Requires-Python: >=3.7
976 Description-Content-Type: text/markdown
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.
+0
-68
examples/README.md less more
0 # Examples
1
2 ## Built-in functions
3
4 - [ping](ping.py)
5 - [multiping](multiping.py)
6 - [traceroute](traceroute.py)
7
8 ## Sockets (advanced)
9
10 - [verbose-ping](verbose_ping.py)
11
12 This example shows how to reproduce the ping command using sockets of `icmplib`.
13
14 *Output:*
15
16 ```console
17 PING 1.1.1.1: 56 data bytes
18
19 64 bytes from 1.1.1.1: icmp_seq=0 time=12.061 ms
20 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms
21 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms
22 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms
23 ```
24
25 - [verbose-traceroute](verbose_traceroute.py)
26
27 This example shows how to reproduce the traceroute command.
28
29 *Output:*
30
31 ```console
32 Traceroute to ovh.com (198.27.92.1): 56 data bytes, 30 hops max
33
34 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
38 5 * * *
39 6 37.187.231.75 be5.rbx-iplb1-a70.fr.eu 17.1 ms
40 7 198.27.92.1 www.ovh.com 10.87 ms
41 ```
42
43 - [broadcast-ping](broadcast_ping.py)
44
45 This example shows how to enable the broadcast support on a socket.
46
47 *Output:*
48
49 ```console
50 PING 255.255.255.255: 56 data bytes
51
52 64 bytes from 10.0.0.17: icmp_seq=0 time=1.065 ms
53 64 bytes from 10.0.0.40: icmp_seq=0 time=1.595 ms
54 64 bytes from 10.0.0.41: icmp_seq=0 time=9.471 ms
55
56 64 bytes from 10.0.0.17: icmp_seq=1 time=0.983 ms
57 64 bytes from 10.0.0.40: icmp_seq=1 time=1.579 ms
58 64 bytes from 10.0.0.41: icmp_seq=1 time=9.345 ms
59
60 64 bytes from 10.0.0.17: icmp_seq=2 time=0.916 ms
61 64 bytes from 10.0.0.40: icmp_seq=2 time=2.031 ms
62 64 bytes from 10.0.0.41: icmp_seq=2 time=9.554 ms
63
64 64 bytes from 10.0.0.17: icmp_seq=3 time=1.112 ms
65 64 bytes from 10.0.0.40: icmp_seq=3 time=1.384 ms
66 64 bytes from 10.0.0.41: icmp_seq=3 time=9.565 ms
67 ```
+0
-94
examples/broadcast_ping.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 Example: broadcast ping (advanced)
12
13 PING 255.255.255.255: 56 data bytes
14
15 64 bytes from 10.0.0.17: icmp_seq=0 time=1.065 ms
16 64 bytes from 10.0.0.40: icmp_seq=0 time=1.595 ms
17 64 bytes from 10.0.0.41: icmp_seq=0 time=9.471 ms
18
19 64 bytes from 10.0.0.17: icmp_seq=1 time=0.983 ms
20 64 bytes from 10.0.0.40: icmp_seq=1 time=1.579 ms
21 64 bytes from 10.0.0.41: icmp_seq=1 time=9.345 ms
22
23 64 bytes from 10.0.0.17: icmp_seq=2 time=0.916 ms
24 64 bytes from 10.0.0.40: icmp_seq=2 time=2.031 ms
25 64 bytes from 10.0.0.41: icmp_seq=2 time=9.554 ms
26
27 64 bytes from 10.0.0.17: icmp_seq=3 time=1.112 ms
28 64 bytes from 10.0.0.40: icmp_seq=3 time=1.384 ms
29 64 bytes from 10.0.0.41: icmp_seq=3 time=9.565 ms
30 '''
31
32 from icmplib import (
33 ICMPv4Socket,
34 ICMPRequest,
35 TimeoutExceeded,
36 ICMPLibError,
37 PID)
38
39
40 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')
44
45 # Broadcast is only possible in IPv4
46 socket = ICMPv4Socket()
47
48 # We allow the socket to send broadcast packets
49 socket.broadcast = True
50
51 for sequence in range(count):
52 # We create an ICMP request
53 request = ICMPRequest(
54 destination=address,
55 id=id,
56 sequence=sequence,
57 timeout=timeout)
58
59 print()
60
61 try:
62 # We send the request
63 socket.send(request)
64
65 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()
70
71 # We calculate the round-trip time of the reply
72 round_trip_time = (reply.time - request.time) * 1000
73
74 # We display some information
75 print(f' {reply.bytes_received} bytes from '
76 f'{reply.source}: icmp_seq={sequence} '
77 f'time={round(round_trip_time, 3)} ms')
78
79 except TimeoutExceeded:
80 # The timeout has been reached
81 # We use this exception to break the while loop
82 pass
83
84 except ICMPLibError:
85 # All other errors
86 print('An error has occurred.')
87
88
89 # Limited broadcast
90 broadcast_ping('255.255.255.255')
91
92 # Directed broadcast
93 # broadcast_ping('192.168.0.255')
+0
-50
examples/multiping.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 Example: multiping
12 '''
13
14 from icmplib import multiping
15
16
17 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
23 # IPv4 addresses
24 '1.1.1.1',
25 '8.8.8.8',
26 '10.0.0.100',
27 '10.0.0.200',
28
29 # IPv6 addresses
30 '::1',
31 ]
32
33 hosts = multiping(addresses, count=2, interval=0.5, timeout=1)
34
35 hosts_alive = []
36 hosts_dead = []
37
38 for host in hosts:
39 if host.is_alive:
40 hosts_alive.append(host.address)
41
42 else:
43 hosts_dead.append(host.address)
44
45 print(hosts_alive)
46 # ['github.com', '1.1.1.1', '8.8.8.8', '::1']
47
48 print(hosts_dead)
49 # ['10.0.0.100', '10.0.0.200']
+0
-52
examples/ping.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 Example: ping
12 '''
13
14 from icmplib import ping
15
16
17 host = ping('1.1.1.1', count=10, interval=0.5, timeout=1)
18
19 # The IP address of the gateway or host that responded to the request
20 print(host.address)
21 # '1.1.1.1'
22
23 # The minimum round-trip time
24 print(host.min_rtt)
25 # 12.2
26
27 # The average round-trip time
28 print(host.avg_rtt)
29 # 13.2
30
31 # The maximum round-trip time
32 print(host.max_rtt)
33 # 17.6
34
35 # The number of packets transmitted to the destination host
36 print(host.packets_sent)
37 # 10
38
39 # The number of packets sent by the remote host and received by the
40 # current host
41 print(host.packets_received)
42 # 9
43
44 # Packet loss occurs when packets fail to reach their destination
45 # Returns a float between 0 and 1 (all packets are lost)
46 print(host.packet_loss)
47 # 0.1
48
49 # Indicates whether the host is reachable
50 print(host.is_alive)
51 # True
+0
-45
examples/traceroute.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 Example: traceroute
12 '''
13
14 from icmplib import traceroute
15
16
17 hops = traceroute('1.1.1.1', timeout=1, fast_mode=True)
18
19 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
25
26 last_distance = 0
27
28 for hop in hops:
29 if last_distance + 1 != hop.distance:
30 print(' * Some routers are not responding')
31
32 print(f'{hop.distance:4} {hop.address:15} '
33 f'{hop.avg_rtt} ms')
34
35 last_distance = hop.distance
36
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
+0
-93
examples/verbose_ping.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 Example: verbose ping (advanced)
12
13 PING 1.1.1.1: 56 data bytes
14
15 64 bytes from 1.1.1.1: icmp_seq=0 time=12.061 ms
16 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms
17 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms
18 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms
19 '''
20
21 from icmplib import (
22 ICMPv4Socket,
23 ICMPv6Socket,
24 ICMPRequest,
25 TimeoutExceeded,
26 ICMPError,
27 ICMPLibError,
28 is_ipv6_address,
29 PID)
30
31 from time import sleep
32
33
34 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
37 print(f'PING {address}: 56 data bytes\n')
38
39 # Detection of the socket to use
40 if is_ipv6_address(address):
41 socket = ICMPv6Socket()
42
43 else:
44 socket = ICMPv4Socket()
45
46 for sequence in range(count):
47 # We create an ICMP request
48 request = ICMPRequest(
49 destination=address,
50 id=id,
51 sequence=sequence,
52 timeout=timeout)
53
54 try:
55 # We send the request
56 socket.send(request)
57
58 # We are awaiting receipt of an ICMP reply
59 reply = socket.receive()
60
61 # We received a reply
62 # We display some information
63 print(f' {reply.bytes_received} bytes from '
64 f'{reply.source}: ', end='')
65
66 # We throw an exception if it is an ICMP error message
67 reply.raise_for_status()
68
69 # We calculate the round-trip time and we display it
70 round_trip_time = (reply.time - request.time) * 1000
71
72 print(f'icmp_seq={sequence} '
73 f'time={round(round_trip_time, 3)} ms')
74
75 # We pause before continuing
76 if sequence < count - 1:
77 sleep(interval)
78
79 except TimeoutExceeded:
80 # The timeout has been reached
81 print(f'Request timeout for icmp_seq {sequence}')
82
83 except ICMPError as err:
84 # An ICMP error message has been received
85 print(err)
86
87 except ICMPLibError:
88 # All other errors
89 print('An error has occurred.')
90
91
92 verbose_ping('1.1.1.1')
+0
-120
examples/verbose_traceroute.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 Example: verbose traceroute (advanced)
12
13 Traceroute to ovh.com (198.27.92.1): 56 data bytes, 30 hops max
14
15 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 5 * * *
20 6 37.187.231.75 be5.rbx-iplb1-a70.fr.eu 17.1 ms
21 7 198.27.92.1 www.ovh.com 10.87 ms
22 '''
23
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
35 from time import sleep
36
37
38 def verbose_traceroute(address, count=3, interval=0.05, timeout=2,
39 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)}): '
43 f'56 data bytes, {max_hops} hops max\n')
44
45 # Detection of the socket to use
46 if is_ipv6_address(address):
47 socket = ICMPv6Socket()
48
49 else:
50 socket = ICMPv4Socket()
51
52 ttl = 1
53 host_reached = False
54
55 while not host_reached and ttl <= max_hops:
56 for sequence in range(count):
57 # We create an ICMP request
58 request = ICMPRequest(
59 destination=address,
60 id=id,
61 sequence=sequence,
62 timeout=timeout,
63 ttl=ttl)
64
65 try:
66 # We send the request
67 socket.send(request)
68
69 # We are awaiting receipt of an ICMP reply
70 reply = socket.receive()
71
72 # We received a reply
73 # We display some information
74 source_name = getfqdn(reply.source)
75
76 print(f'{ttl:3} {reply.source:15} '
77 f'{source_name:40} ', end='')
78
79 # We throw an exception if it is an ICMP error message
80 reply.raise_for_status()
81
82 # We reached the destination host
83 # We calculate the round-trip time and we display it
84 round_trip_time = (reply.time - request.time) * 1000
85 print(round(round_trip_time, 2), 'ms')
86
87 # We can stop the search
88 host_reached = True
89 break
90
91 except TimeExceeded as err:
92 # An ICMP Time Exceeded message has been received
93 # The message was probably generated by an intermediate
94 # gateway
95 reply = err.reply
96
97 # We calculate the round-trip time and we display it
98 round_trip_time = (reply.time - request.time) * 1000
99 print(round(round_trip_time, 2), 'ms')
100
101 sleep(interval)
102 break
103
104 except TimeoutExceeded:
105 # The timeout has been reached and no host or gateway
106 # has responded after multiple attemps
107 if sequence >= count - 1:
108 print(f'{ttl:3} * * *')
109
110 except ICMPLibError:
111 # Other errors are ignored
112 pass
113
114 ttl += 1
115
116 print()
117
118
119 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 ~~~~~~~
2326 <https://www.gnu.org/licenses/>.
2427 '''
2528
26 from .sockets import ICMPv4Socket, ICMPv6Socket
29 from .sockets import ICMPv4Socket, ICMPv6Socket, AsyncSocket
2730 from .models import ICMPRequest, ICMPReply, Host, Hop
28 from .ping import ping, multiping
31 from .ping import ping, async_ping
32 from .multiping import multiping, async_multiping
2933 from .traceroute import traceroute
3034 from .exceptions import *
31 from .utils import PID, is_ipv4_address, is_ipv6_address
35 from .utils import is_hostname, is_ipv4_address, is_ipv6_address
36 from .utils import PID, resolve, async_resolve
3237
3338
3439 __author__ = 'Valentin BELYN'
35 __copyright__ = 'Copyright 2017-2020 Valentin BELYN'
40 __copyright__ = 'Copyright 2017-2021 Valentin BELYN'
3641 __license__ = 'GNU Lesser General Public License v3.0'
3742
38 __version__ = '1.2.2'
39 __build__ = '201010'
43 __version__ = '3.0-alpha'
44 __build__ = '210515'
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 resolved.
49 This concerns both Fully Qualified Domain Names and hostnames.
50
51 '''
52 def __init__(self, name):
53 message = f'The name \'{name}\' cannot be resolved'
54 super().__init__(message)
55
56
4357 class ICMPSocketError(ICMPLibError):
4458 '''
4559 Base class for ICMP sockets exceptions.
4761 '''
4862
4963
64 class SocketAddressError(ICMPSocketError):
65 '''
66 Raised when the requested address cannot be assigned to the socket.
67
68 '''
69 def __init__(self, address):
70 message = f'The requested address ({address}) cannot be ' \
71 'assigned to the socket'
72 super().__init__(message)
73
74
5075 class SocketPermissionError(ICMPSocketError):
5176 '''
52 Raised when the permissions are insufficient to create a socket.
77 Raised when the privileges are insufficient to create the socket.
5378
5479 '''
5580 def __init__(self):
6994
7095 class SocketBroadcastError(ICMPSocketError):
7196 '''
72 Raised when a broadcast address is used and the corresponding
73 option is not enabled on the socket.
97 Raised when a broadcast address is used and the corresponding option
98 is not enabled on the socket.
7499
75100 '''
76101 def __init__(self):
77102 message = 'Broadcast is not allowed: ' \
78 'please use broadcast method (setter) to allow it'
103 'please use the \'broadcast\' property to allow it'
79104 super().__init__(message)
80105
81106
161186 '''
162187 Base class for ICMP Time Exceeded messages.
163188
164 Time Exceeded message is generated by a gateway to inform the
165 source of a discarded datagram due to the time to live field
166 reaching zero. A Time Exceeded message may also be sent by a host
167 if it fails to reassemble a fragmented datagram within its time
168 limit.
189 Time Exceeded message is generated by a gateway to inform the source
190 of a discarded datagram due to the time to live field reaching zero.
191 A Time Exceeded message may also be sent by a host if it fails to
192 reassemble a fragmented datagram within its time limit.
169193
170194 '''
171195 _CODES = {}
+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
7070 Only available on Unix systems. Ignored on Windows.
7171
7272 '''
73 __slots__ = '_destination', '_id', '_sequence', '_payload', \
74 '_payload_size', '_ttl', '_traffic_class', '_time'
75
7376 def __init__(self, destination, id, sequence, payload=None,
74 payload_size=56, timeout=2, ttl=64, traffic_class=0):
75
76 id &= 0xffff
77 sequence &= 0xffff
77 payload_size=56, ttl=64, traffic_class=0):
7878
7979 if payload:
8080 payload_size = len(payload)
8181
8282 self._destination = destination
83 self._id = id
84 self._sequence = sequence
83 self._id = id & 0xffff
84 self._sequence = sequence & 0xffff
8585 self._payload = payload
8686 self._payload_size = payload_size
87 self._timeout = timeout
8887 self._ttl = ttl
8988 self._traffic_class = traffic_class
9089 self._time = 0
9594 @property
9695 def destination(self):
9796 '''
98 The IP address of the gateway or host to which the message
99 should be sent.
97 The IP address of the host to which the message should be sent.
10098
10199 '''
102100 return self._destination
137135 return self._payload_size
138136
139137 @property
140 def timeout(self):
141 '''
142 The maximum waiting time for receiving the reply in seconds.
143
144 '''
145 return self._timeout
146
147 @property
148138 def ttl(self):
149139 '''
150 The time to live of the packet in seconds.
140 The time to live of the packet in terms of hops.
151141
152142 '''
153143 return self._ttl
164154 def time(self):
165155 '''
166156 The timestamp of the ICMP request.
157
167158 Initialized to zero when creating the request and replaced by
168 `ICMPv4Socket` or `ICMPv6Socket` with the time of sending.
159 the `send` method of an ICMP socket with the time of sending.
169160
170161 '''
171162 return self._time
173164
174165 class ICMPReply:
175166 '''
176 A class that represents an ICMP reply. Generated from an
177 `ICMPSocket` object (`ICMPv4Socket` or `ICMPv6Socket`).
167 A class that represents an ICMP reply. Generated from an ICMP socket.
178168
179169 :type source: str
180 :param source: The IP address of the gateway or host that composes
181 the ICMP message.
170 :param source: The IP address of the host that composes the ICMP
171 message.
172
173 :type family: int
174 :param family: The address family. Can be set to `4` for IPv4 or `6`
175 for IPv6 addresses.
182176
183177 :type id: int
184 :param id: The identifier of the reply. Used to match the reply
185 with the request.
178 :param id: The identifier of the reply. Used to match the reply with
179 the request.
186180
187181 :type sequence: int
188182 :param sequence: The sequence number. Used to match the reply with
189183 the request.
190184
191185 :type type: int
192 :param type: The type of message.
186 :param type: The type of ICMP message.
193187
194188 :type code: int
195 :param code: The error code.
189 :param code: The ICMP error code.
196190
197191 :type bytes_received: int
198192 :param bytes_received: The number of bytes received.
201195 :param time: The timestamp of the ICMP reply.
202196
203197 '''
204 def __init__(self, source, id, sequence, type, code,
198 __slots__ = '_source', '_family', '_id', '_sequence', '_type', \
199 '_code', '_bytes_received', '_time'
200
201 def __init__(self, source, family, id, sequence, type, code,
205202 bytes_received, time):
206203
207204 self._source = source
205 self._family = family
208206 self._id = id
209207 self._sequence = sequence
210208 self._type = type
217215
218216 def raise_for_status(self):
219217 '''
220 Throw an exception if the reply is not an ICMP ECHO_REPLY.
218 Throw an exception if the reply is not an ICMP Echo Reply.
221219 Otherwise, do nothing.
222220
223 :raises ICMPv4DestinationUnreachable: If the ICMPv4 reply is
224 type 3.
225 :raises ICMPv4TimeExceeded: If the ICMPv4 reply is type 11.
226 :raises ICMPv6DestinationUnreachable: If the ICMPv6 reply is
227 type 1.
228 :raises ICMPv6TimeExceeded: If the ICMPv6 reply is type 3.
229 :raises ICMPError: If the reply is of another type and is not
230 an ICMP ECHO_REPLY.
231
232 '''
233 if is_ipv6_address(self._source):
234 echo_reply_type = 129
235 errors = {
236 1: ICMPv6DestinationUnreachable,
237 3: ICMPv6TimeExceeded
238 }
239
221 :raises DestinationUnreachable: If the destination is
222 unreachable for some reason.
223 :raises TimeExceeded: If the time to live field of the ICMP
224 request has reached zero.
225 :raises ICMPError: Raised for any other type and ICMP error
226 code, except ICMP Echo Reply messages.
227
228 '''
229 if self._family == 6:
230 if self._type == 1:
231 raise ICMPv6DestinationUnreachable(self)
232
233 if self._type == 3:
234 raise ICMPv6TimeExceeded(self)
240235 else:
241 echo_reply_type = 0
242 errors = {
243 3: ICMPv4DestinationUnreachable,
244 11: ICMPv4TimeExceeded
245 }
246
247 if self._type in errors:
248 raise errors[self._type](self)
249
250 if self._type != echo_reply_type:
251 message = f'Error type: {self._type}, ' \
252 f'code: {self._code}'
253
236 if self._type == 3:
237 raise ICMPv4DestinationUnreachable(self)
238
239 if self._type == 11:
240 raise ICMPv4TimeExceeded(self)
241
242 if (self._family == 4 and self._type != 0 or
243 self._family == 6 and self._type != 129):
244 message = f'Error type: {self._type}, code: {self._code}'
254245 raise ICMPError(message, self)
255246
256247 @property
257248 def source(self):
258249 '''
259 The IP address of the gateway or host that composes the ICMP
260 message.
250 The IP address of the host that composes the ICMP message.
261251
262252 '''
263253 return self._source
265255 @property
266256 def id(self):
267257 '''
268 The identifier of the request.
258 The identifier of the reply.
269259 Used to match the reply with the request.
270260
271261 '''
283273 @property
284274 def type(self):
285275 '''
286 The type of message.
276 The type of ICMP message.
287277
288278 '''
289279 return self._type
291281 @property
292282 def code(self):
293283 '''
294 The error code.
284 The ICMP error code.
295285
296286 '''
297287 return self._code
305295 return self._bytes_received
306296
307297 @property
308 def received_bytes(self):
309 '''
310 Deprecated: use the `bytes_received` property instead.
311
312 '''
313 print('[icmplib] Deprecation Warning: The `received_bytes` '
314 'property will be removed from icmplib 2.0. Use the '
315 '`bytes_received` property instead.')
316
317 return self._bytes_received
318
319 @property
320298 def time(self):
321299 '''
322300 The timestamp of the ICMP reply.
327305
328306 class Host:
329307 '''
330 A class that represents a host. Simplifies the exploitation of
331 results from `ping` and `traceroute` functions.
308 A class that represents a host. It simplifies the use of the results
309 from the `ping`, `multiping` and `traceroute` functions.
332310
333311 :type address: str
334 :param address: The IP address of the gateway or host that
335 responded to the request.
336
337 :type min_rtt: float
338 :param min_rtt: The minimum round-trip time.
339
340 :type avg_rtt: float
341 :param avg_rtt: The average round-trip time.
342
343 :type max_rtt: float
344 :param max_rtt: The maximum round-trip time.
312 :param address: The IP address of the host that responded to the
313 request.
345314
346315 :type packets_sent: int
347316 :param packets_sent: The number of packets transmitted to the
348317 destination host.
349318
350 :type packets_received: int
351 :param packets_received: The number of packets sent by the remote
352 host and received by the current host.
353
354 '''
355 def __init__(self, address, min_rtt, avg_rtt, max_rtt,
356 packets_sent, packets_received):
357
319 :type rtts: list[float]
320 :param rtts: The list of round-trip times expressed in milliseconds.
321
322 '''
323 __slots__ = '_address', '_packets_sent', '_rtts'
324
325 def __init__(self, address, packets_sent, rtts):
358326 self._address = address
359 self._min_rtt = round(min_rtt, 3)
360 self._avg_rtt = round(avg_rtt, 3)
361 self._max_rtt = round(max_rtt, 3)
362327 self._packets_sent = packets_sent
363 self._packets_received = packets_received
328 self._rtts = rtts
364329
365330 def __repr__(self):
366331 return f'<Host [{self._address}]>'
367332
333 def __str__(self):
334 return f' {self._address}\n' + '-' * 60 + '\n' \
335 f' Packets sent: {self._packets_sent}\n' \
336 f' Packets received: {self.packets_received}\n' \
337 f' Packet loss: {self.packet_loss * 100}%\n' \
338 f' Round-trip times: {self.min_rtt} ms / ' \
339 f'{self.avg_rtt} ms / {self.max_rtt} ms\n' \
340 f' Jitter: {self.jitter} ms\n' + '-' * 60
341
368342 @property
369343 def address(self):
370344 '''
371 The IP address of the gateway or host that responded to the
372 request.
345 The IP address of the host that responded to the request.
373346
374347 '''
375348 return self._address
377350 @property
378351 def min_rtt(self):
379352 '''
380 The minimum round-trip time.
381
382 '''
383 return self._min_rtt
353 The minimum round-trip time in milliseconds.
354
355 '''
356 if not self._rtts:
357 return 0.0
358
359 return round(min(self._rtts), 3)
384360
385361 @property
386362 def avg_rtt(self):
387363 '''
388 The average round-trip time.
389
390 '''
391 return self._avg_rtt
364 The average round-trip time in milliseconds.
365
366 '''
367 if not self._rtts:
368 return 0.0
369
370 return round(sum(self._rtts) / len(self._rtts), 3)
392371
393372 @property
394373 def max_rtt(self):
395374 '''
396 The maximum round-trip time.
397
398 '''
399 return self._max_rtt
375 The maximum round-trip time in milliseconds.
376
377 '''
378 if not self._rtts:
379 return 0.0
380
381 return round(max(self._rtts), 3)
382
383 @property
384 def rtts(self):
385 '''
386 The list of round-trip times expressed in milliseconds.
387
388 '''
389 return self._rtts
400390
401391 @property
402392 def packets_sent(self):
404394 The number of packets transmitted to the destination host.
405395
406396 '''
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
419397 return self._packets_sent
420398
421399 @property
425403 the current host.
426404
427405 '''
428 return self._packets_received
429
430 @property
431 def received_packets(self):
432 '''
433 Deprecated: use the `packets_received` property instead.
434
435 '''
436 print('[icmplib] Deprecation Warning: The `received_packets` '
437 'property will be removed from icmplib 2.0. Use the '
438 '`packets_received` property instead.')
439
440 return self._packets_received
406 return len(self._rtts)
441407
442408 @property
443409 def packet_loss(self):
449415 if not self._packets_sent:
450416 return 0.0
451417
452 return 1 - self._packets_received / self._packets_sent
418 return round(1 - len(self._rtts) / self._packets_sent, 2)
419
420 @property
421 def jitter(self):
422 '''
423 The jitter in milliseconds, defined as the variance of the
424 latency of packets flowing through the network.
425
426 At least two ICMP responses are required to calculate the
427 jitter.
428
429 '''
430 sum_deltas = 0.0
431 num_deltas = len(self._rtts) - 1
432
433 if num_deltas < 1:
434 return 0.0
435
436 for i in range(num_deltas):
437 sum_deltas += abs(self._rtts[i] - self._rtts[i + 1])
438
439 return round(sum_deltas / num_deltas, 3)
453440
454441 @property
455442 def is_alive(self):
456443 '''
457 Indicate whether the host is reachable. Return a `boolean`.
458
459 '''
460 return self._packets_received > 0
444 Indicate whether the host is reachable.
445 Return a `boolean`.
446
447 '''
448 return len(self._rtts) > 0
461449
462450
463451 class Hop(Host):
466454 some features for the `traceroute` function.
467455
468456 :type address: str
469 :param address: The IP address of the gateway or host that
470 responded to the request.
471
472 :type min_rtt: float
473 :param min_rtt: The minimum round-trip time.
474
475 :type avg_rtt: float
476 :param avg_rtt: The average round-trip time.
477
478 :type max_rtt: float
479 :param max_rtt: The maximum round-trip time.
457 :param address: The IP address of the gateway or host that responded
458 to the request.
480459
481460 :type packets_sent: int
482461 :param packets_sent: The number of packets transmitted to the
483462 destination host.
484463
485 :type packets_received: int
486 :param packets_received: The number of packets sent by the remote
487 host and received by the current host.
464 :type rtts: list[float]
465 :param rtts: The list of round-trip times expressed in milliseconds.
488466
489467 :type distance: int
490 :param distance: The distance (in terms of hops) that separates the
468 :param distance: The distance, in terms of hops, that separates the
491469 remote host from the current machine.
492470
493471 '''
494 def __init__(self, address, min_rtt, avg_rtt, max_rtt,
495 packets_sent, packets_received, distance):
496
497 super().__init__(address, min_rtt, avg_rtt, max_rtt,
498 packets_sent, packets_received)
499
472 __slots__ = '_address', '_packets_sent', '_rtts', '_distance'
473
474 def __init__(self, address, packets_sent, rtts, distance):
475 super().__init__(address, packets_sent, rtts)
500476 self._distance = distance
501477
502478 def __repr__(self):
503479 return f'<Hop {self._distance} [{self._address}]>'
504480
481 def __str__(self):
482 return f' #{self._distance:<2} ' + super().__str__()[2:]
483
505484 @property
506485 def distance(self):
507486 '''
508 The distance (in terms of hops) that separates the remote host
487 The distance, in terms of hops, that separates the remote host
509488 from the current machine.
510489
511490 '''
0 '''
1 icmplib
2 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
6
7 https://github.com/ValentinBELYN/icmplib
8
9 :copyright: Copyright 2017-2021 Valentin BELYN.
10 :license: GNU LGPLv3, see the LICENSE for details.
11
12 ~~~~~~~
13
14 This program is free software: you can redistribute it and/or
15 modify it under the terms of the GNU Lesser General Public License
16 as published by the Free Software Foundation, either version 3 of
17 the License, or (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU Lesser General Public License for more details.
23
24 You should have received a copy of the GNU Lesser General Public
25 License along with this program. If not, see
26 <https://www.gnu.org/licenses/>.
27 '''
28
29 import asyncio
30
31 from .ping import async_ping
32
33
34 async def async_multiping(addresses, count=2, interval=0.5, timeout=2,
35 concurrent_tasks=50, source=None, family=None, privileged=True,
36 **kwargs):
37 '''
38 Send ICMP Echo Request packets to several network hosts.
39
40 This function is non-blocking.
41
42 :type addresses: str
43 :param addresses: The IP addresses of the hosts to which messages
44 should be sent. Hostnames and FQDNs are allowed but not
45 recommended. You can easily retrieve their IP address by calling
46 the built-in `resolve` function.
47
48 :type count: int, optional
49 :param count: The number of ping to perform per address.
50 Default to 2.
51
52 :type interval: int or float, optional
53 :param interval: The interval in seconds between sending each
54 packet. Default to 0.5.
55
56 :type timeout: int or float, optional
57 :param timeout: The maximum waiting time for receiving a reply in
58 seconds. Default to 2.
59
60 :type concurrent_tasks: int, optional
61 :param concurrent_tasks: The maximum number of concurrent tasks to
62 speed up processing. This value cannot exceed the maximum number
63 of file descriptors configured on the operating system.
64 Default to 50.
65
66 :type source: str, optional
67 :param source: The IP address from which you want to send packets.
68 By default, the interface is automatically chosen according to
69 the specified destinations. This parameter should not be used if
70 you are passing both IPv4 and IPv6 addresses to this function.
71
72 :type family: int, optional
73 :param family: The address family if a hostname or FQDN is specified.
74 Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default,
75 this function searches for IPv4 addresses first before searching
76 for IPv6 addresses.
77
78 :type privileged: bool, optional
79 :param privileged: When this option is enabled, this library fully
80 manages the exchanges and the structure of ICMP packets.
81 Disable this option if you want to use this function without
82 root privileges and let the kernel handle ICMP headers.
83 Default to True.
84 Only available on Unix systems. Ignored on Windows.
85
86 Advanced (**kwags):
87
88 :type payload: bytes, optional
89 :param payload: The payload content in bytes. A random payload is
90 used by default.
91
92 :type payload_size: int, optional
93 :param payload_size: The payload size. Ignored when the `payload`
94 parameter is set. Default to 56.
95
96 :type traffic_class: int, optional
97 :param traffic_class: The traffic class of ICMP packets.
98 Provides a defined level of service to packets by setting the
99 DS Field (formerly TOS) or the Traffic Class field of IP
100 headers. Packets are delivered with the minimum priority by
101 default (Best-effort delivery).
102 Intermediate routers must be able to support this feature.
103 Only available on Unix systems. Ignored on Windows.
104
105 :rtype: list[Host]
106 :returns: A list of `Host` objects containing statistics about the
107 desired destinations. The list is sorted in the same order as
108 the addresses passed in parameters.
109
110 :raises NameLookupError: If you pass a hostname or FQDN in
111 parameters and it does not exist or cannot be resolved.
112 :raises SocketPermissionError: If the privileges are insufficient to
113 create the socket.
114 :raises SocketAddressError: If the source address cannot be assigned
115 to the socket.
116 :raises ICMPSocketError: If another error occurs. See the
117 `ICMPv4Socket` or `ICMPv6Socket` class for details.
118
119 Usage::
120
121 >>> import asyncio
122 >>> from icmplib import async_multiping
123 >>> hosts = asyncio.run(async_multiping(['10.0.0.5', '::1']))
124
125 >>> for host in hosts:
126 ... if host.is_alive:
127 ... print(f'{host.address} is up!')
128 ... else:
129 ... print(f'{host.address} is down!')
130
131 10.0.0.5 is down!
132 ::1 is up!
133
134 See the `Host` class for details.
135
136 '''
137 loop = asyncio.get_running_loop()
138 tasks = []
139 tasks_pending = set()
140
141 for address in addresses:
142 if len(tasks_pending) >= concurrent_tasks:
143 _, tasks_pending = await asyncio.wait(
144 tasks_pending,
145 return_when=asyncio.FIRST_COMPLETED)
146
147 task = loop.create_task(
148 async_ping(
149 address=address,
150 count=count,
151 interval=interval,
152 timeout=timeout,
153 source=source,
154 family=family,
155 privileged=privileged,
156 **kwargs))
157
158 tasks.append(task)
159 tasks_pending.add(task)
160
161 await asyncio.wait(tasks_pending)
162
163 return [task.result() for task in tasks]
164
165
166 def multiping(addresses, count=2, interval=0.5, timeout=2,
167 concurrent_tasks=50, source=None, family=None, privileged=True,
168 **kwargs):
169 '''
170 Send ICMP Echo Request packets to several network hosts.
171
172 :type addresses: str
173 :param addresses: The IP addresses of the hosts to which messages
174 should be sent. Hostnames and FQDNs are allowed but not
175 recommended. You can easily retrieve their IP address by calling
176 the built-in `resolve` function.
177
178 :type count: int, optional
179 :param count: The number of ping to perform per address.
180 Default to 2.
181
182 :type interval: int or float, optional
183 :param interval: The interval in seconds between sending each
184 packet. Default to 0.5.
185
186 :type timeout: int or float, optional
187 :param timeout: The maximum waiting time for receiving a reply in
188 seconds. Default to 2.
189
190 :type concurrent_tasks: int, optional
191 :param concurrent_tasks: The maximum number of concurrent tasks to
192 speed up processing. This value cannot exceed the maximum number
193 of file descriptors configured on the operating system.
194 Default to 50.
195
196 :type source: str, optional
197 :param source: The IP address from which you want to send packets.
198 By default, the interface is automatically chosen according to
199 the specified destinations. This parameter should not be used if
200 you are passing both IPv4 and IPv6 addresses to this function.
201
202 :type family: int, optional
203 :param family: The address family if a hostname or FQDN is specified.
204 Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default,
205 this function searches for IPv4 addresses first before searching
206 for IPv6 addresses.
207
208 :type privileged: bool, optional
209 :param privileged: When this option is enabled, this library fully
210 manages the exchanges and the structure of ICMP packets.
211 Disable this option if you want to use this function without
212 root privileges and let the kernel handle ICMP headers.
213 Default to True.
214 Only available on Unix systems. Ignored on Windows.
215
216 Advanced (**kwags):
217
218 :type payload: bytes, optional
219 :param payload: The payload content in bytes. A random payload is
220 used by default.
221
222 :type payload_size: int, optional
223 :param payload_size: The payload size. Ignored when the `payload`
224 parameter is set. Default to 56.
225
226 :type traffic_class: int, optional
227 :param traffic_class: The traffic class of ICMP packets.
228 Provides a defined level of service to packets by setting the
229 DS Field (formerly TOS) or the Traffic Class field of IP
230 headers. Packets are delivered with the minimum priority by
231 default (Best-effort delivery).
232 Intermediate routers must be able to support this feature.
233 Only available on Unix systems. Ignored on Windows.
234
235 :rtype: list[Host]
236 :returns: A list of `Host` objects containing statistics about the
237 desired destinations. The list is sorted in the same order as
238 the addresses passed in parameters.
239
240 :raises NameLookupError: If you pass a hostname or FQDN in
241 parameters and it does not exist or cannot be resolved.
242 :raises SocketPermissionError: If the privileges are insufficient to
243 create the socket.
244 :raises SocketAddressError: If the source address cannot be assigned
245 to the socket.
246 :raises ICMPSocketError: If another error occurs. See the
247 `ICMPv4Socket` or `ICMPv6Socket` class for details.
248
249 Usage::
250
251 >>> from icmplib import multiping
252 >>> hosts = multiping(['10.0.0.5', '127.0.0.1', '::1'])
253
254 >>> for host in hosts:
255 ... if host.is_alive:
256 ... print(f'{host.address} is up!')
257 ... else:
258 ... print(f'{host.address} is down!')
259
260 10.0.0.5 is down!
261 127.0.0.1 is up!
262 ::1 is up!
263
264 See the `Host` class for details.
265
266 '''
267 return asyncio.run(
268 async_multiping(
269 addresses=addresses,
270 count=count,
271 interval=interval,
272 timeout=timeout,
273 concurrent_tasks=concurrent_tasks,
274 source=source,
275 family=family,
276 privileged=privileged,
277 **kwargs))
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
29 import asyncio
2730 from time import sleep
2831
29 from .sockets import ICMPv4Socket, ICMPv6Socket
32 from .sockets import ICMPv4Socket, ICMPv6Socket, AsyncSocket
3033 from .models import ICMPRequest, Host
3134 from .exceptions import *
32 from .utils import PID, resolve, is_ipv6_address
33
34
35 class PingThread(Thread):
36 def __init__(self, **kwargs):
37 super().__init__()
38 self._kwargs = kwargs
39 self._host = None
40
41 def run(self):
42 self._host = ping(**self._kwargs)
43
44 @property
45 def host(self):
46 return self._host
47
48
49 def ping(address, count=4, interval=1, timeout=2, id=PID, **kwargs):
50 '''
51 Send ICMP ECHO_REQUEST packets to a network host.
35 from .utils import *
36
37
38 def ping(address, count=4, interval=1, timeout=2, id=None, source=None,
39 family=None, privileged=True, **kwargs):
40 '''
41 Send ICMP Echo Request packets to a network host.
5242
5343 :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.
44 :param address: The IP address, hostname or FQDN of the host to
45 which messages should be sent. For deterministic behavior,
46 prefer to use an IP address.
47
48 :type count: int, optional
49 :param count: The number of ping to perform. Default to 4.
50
51 :type interval: int or float, optional
52 :param interval: The interval in seconds between sending each
53 packet. Default to 1.
54
55 :type timeout: int or float, optional
56 :param timeout: The maximum waiting time for receiving a reply in
57 seconds. Default to 2.
58
59 :type id: int, optional
60 :param id: The identifier of ICMP requests. Used to match the
61 responses with requests. In practice, a unique identifier should
62 be used for every ping process. On Linux, this identifier is
63 ignored when the `privileged` parameter is disabled. The library
64 handles this identifier itself by default.
65
66 :type source: str, optional
67 :param source: The IP address from which you want to send packets.
68 By default, the interface is automatically chosen according to
69 the specified destination.
70
71 :type family: int, optional
72 :param family: The address family if a hostname or FQDN is specified.
73 Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default,
74 this function searches for IPv4 addresses first before searching
75 for IPv6 addresses.
76
77 :type privileged: bool, optional
78 :param privileged: When this option is enabled, this library fully
79 manages the exchanges and the structure of ICMP packets.
80 Disable this option if you want to use this function without
81 root privileges and let the kernel handle ICMP headers.
82 Default to True.
83 Only available on Unix systems. Ignored on Windows.
84
85 Advanced (**kwags):
86
87 :type payload: bytes, optional
88 :param payload: The payload content in bytes. A random payload is
89 used by default.
90
91 :type payload_size: int, optional
92 :param payload_size: The payload size. Ignored when the `payload`
93 parameter is set. Default to 56.
94
95 :type traffic_class: int, optional
96 :param traffic_class: The traffic class of ICMP packets.
97 Provides a defined level of service to packets by setting the
98 DS Field (formerly TOS) or the Traffic Class field of IP
99 headers. Packets are delivered with the minimum priority by
100 default (Best-effort delivery).
101 Intermediate routers must be able to support this feature.
102 Only available on Unix systems. Ignored on Windows.
75103
76104 :rtype: Host
77105 :returns: A `Host` object containing statistics about the desired
78106 destination.
79107
80 :raises SocketPermissionError: If the permissions are insufficient
81 to create a socket.
108 :raises NameLookupError: If you pass a hostname or FQDN in
109 parameters and it does not exist or cannot be resolved.
110 :raises SocketPermissionError: If the privileges are insufficient to
111 create the socket.
112 :raises SocketAddressError: If the source address cannot be assigned
113 to the socket.
114 :raises ICMPSocketError: If another error occurs. See the
115 `ICMPv4Socket` or `ICMPv6Socket` class for details.
82116
83117 Usage::
84118
85119 >>> from icmplib import ping
86120 >>> host = ping('1.1.1.1')
87
88121 >>> host.avg_rtt
89122 13.2
90
91123 >>> host.is_alive
92124 True
93125
94126 See the `Host` class for details.
95127
96128 '''
97 address = resolve(address)
129 if is_hostname(address):
130 address = resolve(address, family)[0]
98131
99132 if is_ipv6_address(address):
100 socket = ICMPv6Socket()
101
133 _Socket = ICMPv6Socket
102134 else:
103 socket = ICMPv4Socket()
104
135 _Socket = ICMPv4Socket
136
137 id = id or unique_identifier()
105138 packets_sent = 0
106 packets_received = 0
107
108 min_rtt = float('inf')
109 avg_rtt = 0.0
110 max_rtt = 0.0
111
112 for sequence in range(count):
113 request = ICMPRequest(
114 destination=address,
115 id=id,
116 sequence=sequence,
117 timeout=timeout,
118 **kwargs)
119
120 try:
121 socket.send(request)
122 packets_sent += 1
123
124 reply = socket.receive()
125 reply.raise_for_status()
126 packets_received += 1
127
128 round_trip_time = (reply.time - request.time) * 1000
129 avg_rtt += round_trip_time
130 min_rtt = min(round_trip_time, min_rtt)
131 max_rtt = max(round_trip_time, max_rtt)
132
133 if sequence < count - 1:
139 rtts = []
140
141 with _Socket(source, privileged) as sock:
142 for sequence in range(count):
143 if sequence > 0:
134144 sleep(interval)
135145
136 except ICMPLibError:
137 pass
138
139 if packets_received:
140 avg_rtt /= packets_received
141
146 request = ICMPRequest(
147 destination=address,
148 id=id,
149 sequence=sequence,
150 **kwargs)
151
152 try:
153 sock.send(request)
154 packets_sent += 1
155
156 reply = sock.receive(request, timeout)
157 reply.raise_for_status()
158
159 rtt = (reply.time - request.time) * 1000
160 rtts.append(rtt)
161
162 except ICMPLibError:
163 pass
164
165 return Host(address, packets_sent, rtts)
166
167
168 async def async_ping(address, count=4, interval=1, timeout=2, id=None,
169 source=None, family=None, privileged=True, **kwargs):
170 '''
171 Send ICMP Echo Request packets to a network host.
172
173 This function is non-blocking.
174
175 :type address: str
176 :param address: The IP address, hostname or FQDN of the host to
177 which messages should be sent. For deterministic behavior,
178 prefer to use an IP address.
179
180 :type count: int, optional
181 :param count: The number of ping to perform. Default to 4.
182
183 :type interval: int or float, optional
184 :param interval: The interval in seconds between sending each
185 packet. Default to 1.
186
187 :type timeout: int or float, optional
188 :param timeout: The maximum waiting time for receiving a reply in
189 seconds. Default to 2.
190
191 :type id: int, optional
192 :param id: The identifier of ICMP requests. Used to match the
193 responses with requests. In practice, a unique identifier should
194 be used for every ping process. On Linux, this identifier is
195 ignored when the `privileged` parameter is disabled. The library
196 handles this identifier itself by default.
197
198 :type source: str, optional
199 :param source: The IP address from which you want to send packets.
200 By default, the interface is automatically chosen according to
201 the specified destination.
202
203 :type family: int, optional
204 :param family: The address family if a hostname or FQDN is specified.
205 Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default,
206 this function searches for IPv4 addresses first before searching
207 for IPv6 addresses.
208
209 :type privileged: bool, optional
210 :param privileged: When this option is enabled, this library fully
211 manages the exchanges and the structure of ICMP packets.
212 Disable this option if you want to use this function without
213 root privileges and let the kernel handle ICMP headers.
214 Default to True.
215 Only available on Unix systems. Ignored on Windows.
216
217 Advanced (**kwags):
218
219 :type payload: bytes, optional
220 :param payload: The payload content in bytes. A random payload is
221 used by default.
222
223 :type payload_size: int, optional
224 :param payload_size: The payload size. Ignored when the `payload`
225 parameter is set. Default to 56.
226
227 :type traffic_class: int, optional
228 :param traffic_class: The traffic class of ICMP packets.
229 Provides a defined level of service to packets by setting the
230 DS Field (formerly TOS) or the Traffic Class field of IP
231 headers. Packets are delivered with the minimum priority by
232 default (Best-effort delivery).
233 Intermediate routers must be able to support this feature.
234 Only available on Unix systems. Ignored on Windows.
235
236 :rtype: Host
237 :returns: A `Host` object containing statistics about the desired
238 destination.
239
240 :raises NameLookupError: If you pass a hostname or FQDN in
241 parameters and it does not exist or cannot be resolved.
242 :raises SocketPermissionError: If the privileges are insufficient to
243 create the socket.
244 :raises SocketAddressError: If the source address cannot be assigned
245 to the socket.
246 :raises ICMPSocketError: If another error occurs. See the
247 `ICMPv4Socket` or `ICMPv6Socket` class for details.
248
249 Usage::
250
251 >>> import asyncio
252 >>> from icmplib import async_ping
253 >>> host = asyncio.run(async_ping('1.1.1.1'))
254 >>> host.avg_rtt
255 13.2
256 >>> host.is_alive
257 True
258
259 See the `Host` class for details.
260
261 '''
262 if is_hostname(address):
263 address = (await async_resolve(address, family))[0]
264
265 if is_ipv6_address(address):
266 _Socket = ICMPv6Socket
142267 else:
143 min_rtt = 0.0
144
145 host = Host(
146 address=address,
147 min_rtt=min_rtt,
148 avg_rtt=avg_rtt,
149 max_rtt=max_rtt,
150 packets_sent=packets_sent,
151 packets_received=packets_received)
152
153 socket.close()
154
155 return host
156
157
158 def multiping(addresses, count=2, interval=1, timeout=2, id=PID,
159 max_threads=10, **kwargs):
160 '''
161 Send ICMP ECHO_REQUEST packets to multiple network hosts.
162
163 :type addresses: list of str
164 :param addresses: The IP addresses of the gateways or hosts to
165 which messages should be sent.
166
167 :type count: int
168 :param count: (Optional) The number of ping to perform per address.
169
170 :type interval: int or float
171 :param interval: (Optional) The interval in seconds between sending
172 each packet.
173
174 :type timeout: int or float
175 :param timeout: (Optional) The maximum waiting time for receiving
176 a reply in seconds.
177
178 :type id: int
179 :param id: (Optional) The identifier of the requests. This
180 identifier will be incremented by one for each destination.
181
182 :type max_threads: int
183 :param max_threads: (Optional) The number of threads allowed to
184 speed up processing.
185
186 :param **kwargs: (Optional) Advanced use: arguments passed to
187 `ICMPRequest` objects.
188
189 :rtype: list of Host
190 :returns: A list of `Host` objects containing statistics about the
191 desired destinations. The list is sorted in the same order as
192 the addresses passed in parameters.
193
194 :raises SocketPermissionError: If the permissions are insufficient
195 to create a socket.
196
197 Usage::
198
199 >>> from icmplib import multiping
200 >>> hosts = multiping(['10.0.0.5', '127.0.0.1', '::1'])
201
202 >>> for host in hosts:
203 ... if host.is_alive:
204 ... print(f'{host.address} is alive!')
205 ...
206 ... else:
207 ... print(f'{host.address} is dead!')
208 ...
209 10.0.0.5 is dead!
210 127.0.0.1 is alive!
211 ::1 is alive!
212
213 See the `Host` class for details.
214
215 '''
216 hosts = []
217 inactive_threads = []
218 active_threads = []
219
220 for i, address in enumerate(addresses):
221 thread = PingThread(
222 address=address,
223 count=count,
224 interval=interval,
225 timeout=timeout,
226 id=id + i,
227 **kwargs)
228
229 inactive_threads.append(thread)
230
231 while inactive_threads:
232 thread = inactive_threads.pop(0)
233 thread.start()
234 active_threads.append(thread)
235
236 if (inactive_threads and
237 len(active_threads) < max_threads):
238 sleep(0.05)
239 continue
240
241 while active_threads:
242 thread = active_threads.pop(0)
243 thread.join()
244 hosts.append(thread.host)
245
246 return hosts
268 _Socket = ICMPv4Socket
269
270 id = id or unique_identifier()
271 packets_sent = 0
272 rtts = []
273
274 with AsyncSocket(_Socket(source, privileged)) as sock:
275 for sequence in range(count):
276 if sequence > 0:
277 await asyncio.sleep(interval)
278
279 request = ICMPRequest(
280 destination=address,
281 id=id,
282 sequence=sequence,
283 **kwargs)
284
285 try:
286 sock.send(request)
287 packets_sent += 1
288
289 reply = await sock.receive(request, timeout)
290 reply.raise_for_status()
291
292 rtt = (reply.time - request.time) * 1000
293 rtts.append(rtt)
294
295 except ICMPLibError:
296 pass
297
298 return Host(address, packets_sent, rtts)
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 import socket
29 import socket, asyncio
2730 from struct import pack, unpack
2831 from time import time
2932
30 from .ip import IPv4Socket, IPv6Socket
31 from .models import ICMPRequest, ICMPReply
33 from .models import ICMPReply
3234 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
35 from .utils import *
11636
11737
11838 class ICMPSocket:
11939 '''
12040 Base class for ICMP sockets.
12141
122 :type config: ICMPConfig
123 :param config: The ICMP socket configuration used to create and
124 read ICMP packets.
125
126 :raises SocketPermissionError: If the permissions are insufficient
127 to create the socket.
42 :type address: str, optional
43 :param address: The IP address from which you want to listen and
44 send packets. By default, the socket listens on all interfaces.
45
46 :type privileged: bool, optional
47 :param privileged: When this option is enabled, the socket fully
48 manages the exchanges and the structure of the ICMP packets.
49 Disable this option if you want to instantiate and use the
50 socket without root privileges and let the kernel handle ICMP
51 headers. Default to True.
52 Only available on Unix systems. Ignored on Windows.
53
54 :raises SocketPermissionError: If the privileges are insufficient to
55 create the socket.
56 :raises SocketAddressError: If the requested address cannot be
57 assigned to the socket.
58 :raises ICMPSocketError: If another error occurs while creating the
59 socket.
12860
12961 '''
130 def __init__(self, config):
131 self._socket = None
132 self._config = config
133 self._last_request = None
62 __slots__ = '_sock', '_address', '_privileged'
63
64 _IP_VERSION = -1
65 _ICMP_HEADER_OFFSET = -1
66 _ICMP_HEADER_REAL_OFFSET = -1
67
68 _ICMP_CODE_OFFSET = _ICMP_HEADER_OFFSET + 1
69 _ICMP_CHECKSUM_OFFSET = _ICMP_HEADER_OFFSET + 2
70 _ICMP_ID_OFFSET = _ICMP_HEADER_OFFSET + 4
71 _ICMP_SEQUENCE_OFFSET = _ICMP_HEADER_OFFSET + 6
72 _ICMP_PAYLOAD_OFFSET = _ICMP_HEADER_OFFSET + 8
73
74 _ICMP_ECHO_REQUEST = -1
75 _ICMP_ECHO_REPLY = -1
76
77 def __init__(self, address=None, privileged=True):
78 self._sock = None
79 self._address = address
80
81 # The Linux kernel allows unprivileged users to use datagram
82 # sockets (SOCK_DGRAM) to send ICMP requests. This feature is
83 # now supported by the majority of Unix systems.
84 # Windows is not compatible.
85 self._privileged = privileged or PLATFORM_WINDOWS
13486
13587 try:
136 self._socket = config.IP_SOCKET(
137 config.IP_PROTOCOL)
138
139 except OSError:
140 raise SocketPermissionError
88 self._sock = self._create_socket(
89 socket.SOCK_RAW if self._privileged else
90 socket.SOCK_DGRAM)
91
92 if address:
93 self._sock.bind((address, 0))
94
95 except OSError as err:
96 if err.errno in (1, 13, 10013):
97 raise SocketPermissionError
98
99 if err.errno in (-9, 49, 99, 10049, 11001):
100 raise SocketAddressError(address)
101
102 raise ICMPSocketError(str(err))
103
104 def __enter__(self):
105 '''
106 Return this object.
107
108 '''
109 return self
110
111 def __exit__(self, type, value, traceback):
112 '''
113 Call the `close` method.
114
115 '''
116 self.close()
141117
142118 def __del__(self):
143119 '''
146122 '''
147123 self.close()
148124
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)
125 def _create_socket(self, type):
126 '''
127 Create and return a new socket. Must be overridden.
128
129 '''
130 raise NotImplementedError
131
132 def _set_ttl(self, ttl):
133 '''
134 Set the time to live of every IP packet originating from this
135 socket. Must be overridden.
136
137 '''
138 raise NotImplementedError
139
140 def _set_traffic_class(self, traffic_class):
141 '''
142 Set the DS Field (formerly TOS) or the Traffic Class field of
143 every IP packet originating from this socket. Must be
144 overridden.
145
146 '''
147 raise NotImplementedError
157148
158149 def _checksum(self, data):
159150 '''
160 Calculate the checksum of a packet.
161
162 '''
151 Compute the checksum of an ICMP packet. Checksums are used to
152 verify the integrity of packets.
153
154 '''
155 sum = 0
163156 data += b'\x00'
164 end = len(data) - 1
165 sum = 0
166
167 for i in range(0, end, 2):
157
158 for i in range(0, len(data) - 1, 2):
168159 sum += (data[i] << 8) + data[i + 1]
169 sum = (sum >> 16) + (sum & 0xffff)
160 sum = (sum & 0xffff) + (sum >> 16)
170161
171162 sum = ~sum & 0xffff
172163
174165
175166 def _create_packet(self, id, sequence, payload):
176167 '''
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)
168 Build an ICMP packet from an identifier, a sequence number and
169 a payload.
170
171 This method returns the newly created ICMP header concatenated
172 to the payload passed in parameters.
173
174 '''
175 checksum = 0
176
177 # Temporary ICMP header to compute the checksum
178 header = pack('!2B3H', self._ICMP_ECHO_REQUEST, 0, checksum,
179 id, sequence)
180
184181 checksum = self._checksum(header + payload)
185182
186 # Definitive header
187 header = self._create_header(type, 0, checksum, id, sequence)
183 # Definitive ICMP header
184 header = pack('!2B3H', self._ICMP_ECHO_REQUEST, 0, checksum,
185 id, sequence)
188186
189187 return header + payload
190188
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:
189 def _parse_reply(self, packet, source, current_time):
190 '''
191 Parse an ICMP reply from bytes.
192
193 This method returns an `ICMPReply` object or `None` if the reply
194 cannot be parsed.
195
196 '''
197 # On Linux, the IP header is missing when a datagram socket is
198 # used (SOCK_DGRAM). To keep the same behavior on all operating
199 # systems including macOS which has this header, we add a
200 # padding of the size of the missing IP header.
201 if not self._privileged and PLATFORM_LINUX:
202 packet = b'\x00' * self._ICMP_HEADER_OFFSET + packet
203
204 bytes_received = len(packet) - self._ICMP_HEADER_OFFSET
205
206 if len(packet) < self._ICMP_CHECKSUM_OFFSET:
202207 return None
203208
204209 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:
210 self._ICMP_HEADER_OFFSET:
211 self._ICMP_CHECKSUM_OFFSET])
212
213 if type != self._ICMP_ECHO_REPLY:
214 packet = packet[
215 self._ICMP_PAYLOAD_OFFSET
216 - self._ICMP_HEADER_OFFSET
217 + self._ICMP_HEADER_REAL_OFFSET:]
218
219 if len(packet) < self._ICMP_PAYLOAD_OFFSET:
212220 return None
213221
214222 id, sequence = unpack('!2H', packet[
215 self._config.ICMP_ID_OFFSET:
216 self._config.ICMP_PAYLOAD_OFFSET
217 ])
218
219 reply = ICMPReply(
223 self._ICMP_ID_OFFSET:
224 self._ICMP_PAYLOAD_OFFSET])
225
226 return ICMPReply(
220227 source=source,
228 family=self._IP_VERSION,
221229 id=id,
222230 sequence=sequence,
223231 type=type,
224232 code=code,
225233 bytes_received=bytes_received,
226 time=reply_time)
227
228 return reply
234 time=current_time)
229235
230236 def send(self, request):
231237 '''
232 Send a request to a host.
238 Send an ICMP request message over the network to a remote host.
233239
234240 This operation is non-blocking. Use the `receive` method to get
235241 the reply.
236242
237243 :type request: ICMPRequest
238 :param request: The ICMP request you have created.
239
240 :raises SocketBroadcastError: If a broadcast address is used
241 and the corresponding option is not enabled on the socket
244 :param request: The ICMP request you have created. If the socket
245 is used in non-privileged mode on a Linux system, the
246 identifier defined in the request will be replaced by the
247 kernel.
248
249 :raises SocketBroadcastError: If a broadcast address is used and
250 the corresponding option is not enabled on the socket
242251 (ICMPv4 only).
243252 :raises SocketUnavailableError: If the socket is closed.
244253 :raises ICMPSocketError: If another error occurs while sending.
245254
246255 '''
247 if not self._socket:
256 if not self._sock:
248257 raise SocketUnavailableError
249258
250 payload = request.payload
251
252 if not payload:
253 payload = random_byte_message(
254 size=request.payload_size)
259 payload = request.payload or \
260 random_byte_message(request.payload_size)
255261
256262 packet = self._create_packet(
257263 id=request.id,
258264 sequence=request.sequence,
259265 payload=payload)
260266
261 self._socket.ttl = request.ttl
262 self._socket.traffic_class = request.traffic_class
263
264 request._time = time()
265
266267 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
268 self._set_ttl(request.ttl)
269 self._set_traffic_class(request.traffic_class)
270
271 request._time = time()
272 self._sock.sendto(packet, (request.destination, 0))
273
274 # On Linux, the ICMP request identifier is replaced by the
275 # kernel with a random port number when a datagram socket is
276 # used (SOCK_DGRAM). So, we update the request created by
277 # the user to take this new identifier into account.
278 if not self._privileged and PLATFORM_LINUX:
279 request._id = self._sock.getsockname()[1]
276280
277281 except PermissionError:
278282 raise SocketBroadcastError
280284 except OSError as err:
281285 raise ICMPSocketError(str(err))
282286
283 def receive(self):
284 '''
285 Receive a reply from a host.
287 def receive(self, request=None, timeout=2):
288 '''
289 Receive an ICMP reply message from the socket.
286290
287291 This method can be called multiple times if you expect several
288 responses (as with a broadcast address).
292 responses as with a broadcast address.
293
294 :type request: ICMPRequest, optional
295 :param request: The ICMP request to use to match the response.
296 By default, all ICMP packets arriving on the socket are
297 returned.
298
299 :type timeout: int or float, optional
300 :param timeout: The maximum waiting time for receiving the
301 response in seconds. Default to 2.
302
303 :rtype: ICMPReply
304 :returns: An `ICMPReply` object representing the response of the
305 desired destination or an upstream gateway. See the
306 `ICMPReply` class for details.
289307
290308 :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.
309 timeout specified in parameters.
294310 :raises SocketUnavailableError: If the socket is closed.
295 :raises ICMPSocketError: If another error occurs while
296 receiving.
297
298 :rtype: ICMPReply
299 :returns: An `ICMPReply` object containing the reply of the
300 desired destination.
301
302 See the `ICMPReply` class for details.
303
304 '''
305 if not self._socket:
311 :raises ICMPSocketError: If another error occurs while receiving.
312
313 '''
314 if not self._sock:
306315 raise SocketUnavailableError
307316
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
317 self._sock.settimeout(timeout)
318 time_limit = time() + timeout
316319
317320 try:
318321 while True:
319 packet, address, port = self._socket.receive()
320 reply_time = time()
321
322 if reply_time > timeout:
322 response = self._sock.recvfrom(1024)
323 current_time = time()
324
325 packet = response[0]
326 source = response[1][0]
327
328 if current_time > time_limit:
323329 raise socket.timeout
324330
325 reply = self._read_reply(
331 reply = self._parse_reply(
326332 packet=packet,
327 source=address,
328 reply_time=reply_time)
329
330 if (reply and request.id == reply.id and
333 source=source,
334 current_time=current_time)
335
336 if (reply and not request or
337 reply and request.id == reply.id and
331338 request.sequence == reply.sequence):
332339 return reply
333340
334341 except socket.timeout:
335 raise TimeoutExceeded(request.timeout)
342 raise TimeoutExceeded(timeout)
336343
337344 except OSError as err:
338345 raise ICMPSocketError(str(err))
342349 Close the socket. It cannot be used after this call.
343350
344351 '''
345 if self._socket:
346 self._socket.close()
347 self._socket = None
352 if self._sock:
353 self._sock.close()
354 self._sock = None
355
356 @property
357 def blocking(self):
358 '''
359 Indicate whether the socket is in blocking mode.
360 Return a `boolean`.
361
362 '''
363 return self._sock.getblocking()
364
365 @blocking.setter
366 def blocking(self, enable):
367 return self._sock.setblocking(enable)
368
369 @property
370 def address(self):
371 '''
372 The IP address from which the socket listens and sends packets.
373 Return `None` if the socket listens on all interfaces.
374
375 '''
376 return self._address
377
378 @property
379 def is_privileged(self):
380 '''
381 Indicate whether the socket is running in privileged mode.
382 Return a `boolean`.
383
384 '''
385 return self._privileged
348386
349387 @property
350388 def is_closed(self):
351389 '''
352 Indicate whether the socket is closed. Return a `boolean`.
353
354 '''
355 return self._socket is None
390 Indicate whether the socket is closed.
391 Return a `boolean`.
392
393 '''
394 return self._sock is None
395
396
397 # Echo Request and Echo Reply messages 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 # | Identifier | Sequence Number |
405 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
406 # | Data ...
407 # +-+-+-+-+-
408 #
409 # ICMPv4 Error message RFC 792
410 #
411 # 0 1 2 3
412 # 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
413 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
414 # | Type | Code | Checksum |
415 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
416 # | Unused / Depends on the error |
417 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
418 # | Internet Header + 64 bits of Original Data Datagram |
419 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
356420
357421
358422 class ICMPv4Socket(ICMPSocket):
359423 '''
360 Socket for sending and receiving ICMPv4 packets.
361
362 :raises SocketPermissionError: If the permissions are insufficient
363 to create the socket.
424 Class for sending and receiving ICMPv4 packets.
425
426 :type address: str, optional
427 :param address: The IP address from which you want to listen and
428 send packets. By default, the socket listens on all interfaces.
429
430 :type privileged: bool, optional
431 :param privileged: When this option is enabled, the socket fully
432 manages the exchanges and the structure of the ICMP packets.
433 Disable this option if you want to instantiate and use the
434 socket without root privileges and let the kernel handle ICMP
435 headers. Default to True.
436 Only available on Unix systems. Ignored on Windows.
437
438 :raises SocketPermissionError: If the privileges are insufficient to
439 create the socket.
440 :raises SocketAddressError: If the requested address cannot be
441 assigned to the socket.
442 :raises ICMPSocketError: If another error occurs while creating the
443 socket.
364444
365445 '''
366 def __init__(self):
367 super().__init__(ICMPv4Config)
446 __slots__ = '_sock', '_address', '_privileged'
447
448 _IP_VERSION = 4
449 _ICMP_HEADER_OFFSET = 20
450 _ICMP_HEADER_REAL_OFFSET = 20
451
452 _ICMP_CODE_OFFSET = _ICMP_HEADER_OFFSET + 1
453 _ICMP_CHECKSUM_OFFSET = _ICMP_HEADER_OFFSET + 2
454 _ICMP_ID_OFFSET = _ICMP_HEADER_OFFSET + 4
455 _ICMP_SEQUENCE_OFFSET = _ICMP_HEADER_OFFSET + 6
456 _ICMP_PAYLOAD_OFFSET = _ICMP_HEADER_OFFSET + 8
457
458 _ICMP_ECHO_REQUEST = 8
459 _ICMP_ECHO_REPLY = 0
460
461 def _create_socket(self, type):
462 '''
463 Create and return a new socket.
464
465 '''
466 return socket.socket(
467 family=socket.AF_INET,
468 type=type,
469 proto=socket.IPPROTO_ICMP)
470
471 def _set_ttl(self, ttl):
472 '''
473 Set the time to live of every IP packet originating from this
474 socket.
475
476 '''
477 self._sock.setsockopt(
478 socket.IPPROTO_IP,
479 socket.IP_TTL,
480 ttl)
481
482 def _set_traffic_class(self, traffic_class):
483 '''
484 Set the DS Field (formerly TOS) of every IP packet originating
485 from this socket.
486
487 Only available on Unix systems. Ignored on Windows.
488
489 '''
490 if PLATFORM_WINDOWS:
491 return
492
493 self._sock.setsockopt(
494 socket.IPPROTO_IP,
495 socket.IP_TOS,
496 traffic_class)
368497
369498 @property
370499 def broadcast(self):
377506 icmp_socket.broadcast = True
378507
379508 '''
380 return self._socket.broadcast
509 return self._sock.getsockopt(
510 socket.SOL_SOCKET,
511 socket.SO_BROADCAST) > 0
381512
382513 @broadcast.setter
383514 def broadcast(self, allow):
384 self._socket.broadcast = allow
515 self._sock.setsockopt(
516 socket.SOL_SOCKET,
517 socket.SO_BROADCAST,
518 allow)
519
520
521 # Echo Request and Echo Reply messages RFC 4443
522 #
523 # 0 1 2 3
524 # 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
525 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
526 # | Type | Code | Checksum |
527 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
528 # | Identifier | Sequence Number |
529 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
530 # | Data ...
531 # +-+-+-+-+-
532 #
533 # ICMPv6 Error message RFC 4443
534 #
535 # 0 1 2 3
536 # 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
537 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
538 # | Type | Code | Checksum |
539 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
540 # | Unused / Depends on the error |
541 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
542 # | Original packet without exceed the minimum IPv6 MTU |
543 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
544
545 # Windows IPv6 compatibility
546 if PLATFORM_WINDOWS: socket.IPPROTO_IPV6 = 41
385547
386548
387549 class ICMPv6Socket(ICMPSocket):
388550 '''
389 Socket for sending and receiving ICMPv6 packets.
390
391 :raises SocketPermissionError: If the permissions are insufficient
392 to create the socket.
551 Class for sending and receiving ICMPv6 packets.
552
553 :type address: str, optional
554 :param address: The IP address from which you want to listen and
555 send packets. By default, the socket listens on all interfaces.
556
557 :type privileged: bool, optional
558 :param privileged: When this option is enabled, the socket fully
559 manages the exchanges and the structure of the ICMP packets.
560 Disable this option if you want to instantiate and use the
561 socket without root privileges and let the kernel handle ICMP
562 headers. Default to True.
563 Only available on Unix systems. Ignored on Windows.
564
565 :raises SocketPermissionError: If the privileges are insufficient to
566 create the socket.
567 :raises SocketAddressError: If the requested address cannot be
568 assigned to the socket.
569 :raises ICMPSocketError: If another error occurs while creating the
570 socket.
393571
394572 '''
395 def __init__(self):
396 super().__init__(ICMPv6Config)
573 __slots__ = '_sock', '_address', '_privileged'
574
575 _IP_VERSION = 6
576 _ICMP_HEADER_OFFSET = 0
577 _ICMP_HEADER_REAL_OFFSET = 40
578
579 _ICMP_CODE_OFFSET = _ICMP_HEADER_OFFSET + 1
580 _ICMP_CHECKSUM_OFFSET = _ICMP_HEADER_OFFSET + 2
581 _ICMP_ID_OFFSET = _ICMP_HEADER_OFFSET + 4
582 _ICMP_SEQUENCE_OFFSET = _ICMP_HEADER_OFFSET + 6
583 _ICMP_PAYLOAD_OFFSET = _ICMP_HEADER_OFFSET + 8
584
585 _ICMP_ECHO_REQUEST = 128
586 _ICMP_ECHO_REPLY = 129
587
588 def _create_socket(self, type):
589 '''
590 Create and return a new socket.
591
592 '''
593 return socket.socket(
594 family=socket.AF_INET6,
595 type=type,
596 proto=socket.IPPROTO_ICMPV6)
597
598 def _set_ttl(self, ttl):
599 '''
600 Set the time to live of every IP packet originating from this
601 socket.
602
603 '''
604 self._sock.setsockopt(
605 socket.IPPROTO_IPV6,
606 socket.IPV6_UNICAST_HOPS,
607 ttl)
608
609 def _set_traffic_class(self, traffic_class):
610 '''
611 Set the Traffic Class field of every IP packet originating from
612 this socket.
613
614 Only available on Unix systems. Ignored on Windows.
615
616 '''
617 if PLATFORM_WINDOWS:
618 return
619
620 self._sock.setsockopt(
621 socket.IPPROTO_IPV6,
622 socket.IPV6_TCLASS,
623 traffic_class)
624
625
626 class AsyncSocket:
627 '''
628 A wrapper for ICMP sockets which makes them asynchronous.
629
630 :type icmp_sock: ICMPSocket
631 :param icmp_sock: An ICMP socket. Once the wrapper is instantiated,
632 this socket should no longer be used directly.
633
634 '''
635 __slots__ = '_icmp_sock'
636
637 def __init__(self, icmp_sock):
638 self._icmp_sock = icmp_sock
639 self._icmp_sock.blocking = False
640
641 def __getattr__(self, name):
642 if not self._icmp_sock:
643 raise SocketUnavailableError
644
645 return getattr(self._icmp_sock, name)
646
647 def __enter__(self):
648 '''
649 Return this object.
650
651 '''
652 return self
653
654 def __exit__(self, type, value, traceback):
655 '''
656 Call the `close` method.
657
658 '''
659 self.close()
660
661 def __del__(self):
662 '''
663 Call the `close` method.
664
665 '''
666 self.close()
667
668 async def receive(self, request=None, timeout=2):
669 '''
670 Receive an ICMP reply message from the socket.
671
672 This method can be called multiple times if you expect several
673 responses as with a broadcast address.
674
675 This method is non-blocking.
676
677 :type request: ICMPRequest, optional
678 :param request: The ICMP request to use to match the response.
679 By default, all ICMP packets arriving on the socket are
680 returned.
681
682 :type timeout: int or float, optional
683 :param timeout: The maximum waiting time for receiving the
684 response in seconds. Default to 2.
685
686 :rtype: ICMPReply
687 :returns: An `ICMPReply` object representing the response of the
688 desired destination or an upstream gateway. See the
689 `ICMPReply` class for details.
690 Unlike the `reveive` method of synchronous ICMP sockets, the
691 returned `ICMPReply` object does not specify the IP address
692 of the host that replied to the request message.
693
694 :raises TimeoutExceeded: If no response is received before the
695 timeout specified in parameters.
696 :raises SocketUnavailableError: If the socket is closed.
697 :raises ICMPSocketError: If another error occurs while receiving.
698
699 '''
700 if not self._icmp_sock or not self._icmp_sock._sock:
701 raise SocketUnavailableError
702
703 loop = asyncio.get_running_loop()
704 time_limit = time() + timeout
705 remaining_time = timeout
706
707 try:
708 while True:
709 packet = await asyncio.wait_for(
710 loop.sock_recv(self._icmp_sock._sock, 1024),
711 remaining_time)
712
713 current_time = time()
714
715 if current_time > time_limit:
716 raise asyncio.TimeoutError
717
718 reply = self._parse_reply(
719 packet=packet,
720 source=None,
721 current_time=current_time)
722
723 if (reply and not request or
724 reply and request.id == reply.id and
725 request.sequence == reply.sequence):
726 return reply
727
728 remaining_time = time_limit - current_time
729
730 except asyncio.TimeoutError:
731 raise TimeoutExceeded(timeout)
732
733 except OSError as err:
734 raise ICMPSocketError(str(err))
735
736 def detach(self):
737 '''
738 Detach the socket from the wrapper and return it. The wrapper
739 cannot be used after this call but the socket can be reused for
740 other purposes.
741
742 '''
743 icmp_sock = self._icmp_sock
744
745 if self._icmp_sock:
746 self._icmp_sock = None
747
748 return icmp_sock
749
750 def close(self):
751 '''
752 Detach the underlying socket from the wrapper and close it. Both
753 cannot be used after this call.
754
755 '''
756 if self._icmp_sock:
757 self.detach().close()
758
759 @property
760 def is_closed(self):
761 '''
762 Indicate whether the underlying socket is closed or detached
763 from this wrapper. Return a `boolean`.
764
765 '''
766 return self._icmp_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 ~~~~~~~
2831 from .sockets import ICMPv4Socket, ICMPv6Socket
2932 from .models import ICMPRequest, Hop
3033 from .exceptions import *
31 from .utils import PID, resolve, is_ipv6_address
32
33
34 def traceroute(address, count=3, interval=0.05, timeout=2, id=PID,
35 traffic_class=0, max_hops=30, fast_mode=False, **kwargs):
34 from .utils import *
35
36
37 def traceroute(address, count=2, interval=0.05, timeout=2, first_hop=1,
38 max_hops=30, fast=False, id=None, source=None, family=None,
39 **kwargs):
3640 '''
3741 Determine the route to a destination host.
3842
3943 The Internet is a large and complex aggregation of network hardware,
4044 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.
45 follow can be difficult. This function uses the IP protocol time to
46 live field and attempts to elicit an ICMP Time Exceeded response
47 from each gateway along the path to some host.
48
49 This function requires root privileges to run.
4450
4551 :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.
52 :param address: The IP address, hostname or FQDN of the host to
53 reach. For deterministic behavior, prefer to use an IP address.
54
55 :type count: int, optional
56 :param count: The number of ping to perform per hop. Default to 2.
57
58 :type interval: int or float, optional
59 :param interval: The interval in seconds between sending each
60 packet. Default to 0.05.
61
62 :type timeout: int or float, optional
63 :param timeout: The maximum waiting time for receiving a reply in
64 seconds. Default to 2.
65
66 :type first_hop: int, optional
67 :param first_hop: The initial time to live value used in outgoing
68 probe packets. Default to 1.
69
70 :type max_hops: int, optional
71 :param max_hops: The maximum time to live (max number of hops) used
72 in outgoing probe packets. Default to 30.
73
74 :type fast: bool, optional
75 :param fast: When this option is enabled and an intermediate router
76 has been reached, skip to the next hop rather than perform
77 additional requests. The `count` parameter then becomes the
78 maximum number of requests in the event of no response.
79 Default to False.
80
81 :type id: int, optional
82 :param id: The identifier of ICMP requests. Used to match the
83 responses with requests. In practice, a unique identifier should
84 be used for every traceroute process. The library handles this
85 identifier itself by default.
86
87 :type source: str, optional
88 :param source: The IP address from which you want to send packets.
89 By default, the interface is automatically chosen according to
90 the specified destination.
91
92 :type family: int, optional
93 :param family: The address family if a hostname or FQDN is specified.
94 Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default,
95 this function searches for IPv4 addresses first before searching
96 for IPv6 addresses.
97
98 Advanced (**kwags):
99
100 :type payload: bytes, optional
101 :param payload: The payload content in bytes. A random payload is
102 used by default.
103
104 :type payload_size: int, optional
105 :param payload_size: The payload size. Ignored when the `payload`
106 parameter is set. Default to 56.
107
108 :type traffic_class: int, optional
109 :param traffic_class: The traffic class of ICMP packets.
66110 Provides a defined level of service to packets by setting the
67111 DS Field (formerly TOS) or the Traffic Class field of IP
68112 headers. Packets are delivered with the minimum priority by
70114 Intermediate routers must be able to support this feature.
71115 Only available on Unix systems. Ignored on Windows.
72116
73 :type max_hops: int
74 :param max_hops: (Optional) The maximum time to live (max number of
75 hops) used in outgoing probe packets.
76
77 :type fast_mode: bool
78 :param fast_mode: (Optional) When this option is enabled and an
79 intermediate router has been reached, skip to the next hop
80 rather than perform additional requests. The `count` parameter
81 then becomes the maximum number of requests in case of no
82 responses.
83
84 :param **kwargs: (Optional) Advanced use: arguments passed to
85 `ICMPRequest` objects.
86
87 :rtype: list of Hop
117 :rtype: list[Hop]
88118 :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.
119 desired destination. The list is sorted in ascending order
120 according to the distance, in terms of hops, that separates the
121 remote host from the current machine. Gateways that do not
122 respond to requests are not added to this list.
123
124 :raises NameLookupError: If you pass a hostname or FQDN in
125 parameters and it does not exist or cannot be resolved.
126 :raises SocketPermissionError: If the privileges are insufficient to
127 create the socket.
128 :raises SocketAddressError: If the source address cannot be assigned
129 to the socket.
130 :raises ICMPSocketError: If another error occurs. See the
131 `ICMPv4Socket` or `ICMPv6Socket` class for details.
95132
96133 Usage::
97134
101138
102139 >>> for hop in hops:
103140 ... if last_distance + 1 != hop.distance:
104 ... print('Some routers are not responding')
141 ... print('Some gateways are not responding')
105142 ...
106143 ... print(f'{hop.distance} {hop.address} {hop.avg_rtt} ms')
107144 ...
108145 ... last_distance = hop.distance
109 ...
110 1 10.0.0.1 5.196 ms
111 2 194.149.169.49 7.552 ms
112 3 194.149.166.54 12.21 ms
113 * Some routers are not responding
114 5 212.73.205.22 22.15 ms
115 6 1.1.1.1 13.59 ms
146
147 1 10.0.0.1 5.196 ms
148 2 194.149.169.49 7.552 ms
149 3 194.149.166.54 12.21 ms
150 * Some gateways are not responding
151 5 212.73.205.22 22.15 ms
152 6 1.1.1.1 13.59 ms
116153
117154 See the `Hop` class for details.
118155
119156 '''
120 address = resolve(address)
157 if is_hostname(address):
158 address = resolve(address, family)[0]
121159
122160 if is_ipv6_address(address):
123 socket = ICMPv6Socket()
124
161 _Socket = ICMPv6Socket
125162 else:
126 socket = ICMPv4Socket()
127
128 ttl = 1
163 _Socket = ICMPv4Socket
164
165 id = id or unique_identifier()
166 ttl = first_hop
129167 host_reached = False
130168 hops = []
131169
132 while not host_reached and ttl <= max_hops:
133 hop_address = None
134 packets_sent = 0
135 packets_received = 0
136
137 min_rtt = float('inf')
138 avg_rtt = 0.0
139 max_rtt = 0.0
140
141 for sequence in range(count):
142 request = ICMPRequest(
143 destination=address,
144 id=id,
145 sequence=sequence,
146 timeout=timeout,
147 ttl=ttl,
148 traffic_class=traffic_class,
149 **kwargs)
150
151 try:
152 socket.send(request)
153 packets_sent += 1
154
155 reply = socket.receive()
156 reply.raise_for_status()
157 host_reached = True
158
159 except TimeExceeded:
160 sleep(interval)
161
162 except ICMPLibError:
163 continue
164
165 hop_address = reply.source
166 packets_received += 1
167
168 round_trip_time = (reply.time - request.time) * 1000
169 avg_rtt += round_trip_time
170 min_rtt = min(round_trip_time, min_rtt)
171 max_rtt = max(round_trip_time, max_rtt)
172
173 if fast_mode:
174 break
175
176 if packets_received:
177 avg_rtt /= packets_received
178
179 hop = Hop(
180 address=hop_address,
181 min_rtt=min_rtt,
182 avg_rtt=avg_rtt,
183 max_rtt=max_rtt,
184 packets_sent=packets_sent,
185 packets_received=packets_received,
186 distance=ttl)
187
188 hops.append(hop)
189
190 ttl += 1
191
192 socket.close()
170 with _Socket(source) as sock:
171 while not host_reached and ttl <= max_hops:
172 reply = None
173 packets_sent = 0
174 rtts = []
175
176 for sequence in range(count):
177 request = ICMPRequest(
178 destination=address,
179 id=id,
180 sequence=sequence,
181 ttl=ttl,
182 **kwargs)
183
184 try:
185 sock.send(request)
186 packets_sent += 1
187
188 reply = sock.receive(request, timeout)
189 rtt = (reply.time - request.time) * 1000
190 rtts.append(rtt)
191
192 reply.raise_for_status()
193 host_reached = True
194
195 except TimeExceeded:
196 sleep(interval)
197
198 except ICMPLibError:
199 break
200
201 if reply:
202 hop = Hop(
203 address=reply.source,
204 packets_sent=packets_sent,
205 rtts=rtts,
206 distance=ttl)
207
208 hops.append(hop)
209
210 ttl += 1
193211
194212 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 ~~~~~~~
2326 <https://www.gnu.org/licenses/>.
2427 '''
2528
26 import socket
29 import socket, asyncio
30
31 from threading import Lock
2732 from sys import platform
2833 from os import getpid
2934 from re import match
3035 from random import choices
3136
37 from .exceptions import NameLookupError
38
3239
3340 PID = getpid()
41 PLATFORM_LINUX = platform == 'linux'
42 PLATFORM_MACOS = platform == 'darwin'
3443 PLATFORM_WINDOWS = platform == 'win32'
3544
45 _lock_id = Lock()
46 _current_id = PID
47
3648
3749 def random_byte_message(size):
3850 '''
3951 Generate a random byte sequence of the specified size.
4052
4153 '''
42 bytes_available = (
54 sequence = choices(
4355 b'abcdefghijklmnopqrstuvwxyz'
4456 b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
45 b'1234567890'
46 )
47
48 return bytes(
49 choices(bytes_available, k=size)
50 )
51
52
53 def resolve(name):
54 '''
55 Resolve a hostname or FQDN into an IP address. If several IP
56 addresses are available, only the first one is returned.
57
58 This function searches for IPv4 addresses first for compatibility
59 reasons before searching for IPv6 addresses.
60
61 If no address is found, the name specified in parameter is
62 returned so as not to impact the operation of other functions. This
63 behavior may change in future versions of icmplib.
64
65 '''
66 if is_ipv4_address(name) or is_ipv6_address(name):
67 return name
68
57 b'1234567890', k=size)
58
59 return bytes(sequence)
60
61
62 def unique_identifier():
63 '''
64 Generate a unique identifier between 0 and 65535.
65 The first number generated will be equal to the PID + 1.
66
67 '''
68 global _current_id
69
70 with _lock_id:
71 _current_id += 1
72 _current_id &= 0xffff
73
74 return _current_id
75
76
77 def resolve(name, family=None):
78 '''
79 Resolve a hostname or FQDN to an IP address. Depending on the name
80 specified in parameters, several IP addresses may be returned.
81
82 This function relies on the DNS name server configured on your
83 operating system.
84
85 :type name: str
86 :param name: A hostname or a Fully Qualified Domain Name (FQDN).
87
88 :type family: int, optional
89 :param family: The address family. Can be set to `4` for IPv4 or `6`
90 for IPv6 addresses. By default, this function searches for IPv4
91 addresses first for compatibility reasons (A DNS lookup) before
92 searching for IPv6 addresses (AAAA DNS lookup).
93
94 :rtype: list[str]
95 :returns: A list of IP addresses associated with the name passed as
96 a parameter.
97
98 :raises NameLookupError: If the requested name does not exist or
99 cannot be resolved.
100
101 '''
69102 try:
70 return socket.getaddrinfo(
103 if family == 6:
104 _family = socket.AF_INET6
105 else:
106 _family = socket.AF_INET
107
108 lookup = socket.getaddrinfo(
71109 host=name,
72110 port=None,
73 family=socket.AF_INET,
74 type=socket.SOCK_DGRAM
75 )[0][4][0]
111 family=_family,
112 type=socket.SOCK_DGRAM)
113
114 return [address[4][0] for address in lookup]
76115
77116 except OSError:
78 pass
79
117 if not family:
118 return resolve(name, 6)
119
120 raise NameLookupError(name)
121
122
123 async def async_resolve(name, family=None):
124 '''
125 Resolve a hostname or FQDN to an IP address. Depending on the name
126 specified in parameters, several IP addresses may be returned.
127
128 This function relies on the DNS name server configured on your
129 operating system.
130
131 This function is non-blocking.
132
133 :type name: str
134 :param name: A hostname or a Fully Qualified Domain Name (FQDN).
135
136 :type family: int, optional
137 :param family: The address family. Can be set to `4` for IPv4 or `6`
138 for IPv6 addresses. By default, this function searches for IPv4
139 addresses first for compatibility reasons (A DNS lookup) before
140 searching for IPv6 addresses (AAAA DNS lookup).
141
142 :rtype: list[str]
143 :returns: A list of IP addresses associated with the name passed as
144 a parameter.
145
146 :raises NameLookupError: If the requested name does not exist or
147 cannot be resolved.
148
149 '''
80150 try:
81 return socket.getaddrinfo(
151 if family == 6:
152 _family = socket.AF_INET6
153 else:
154 _family = socket.AF_INET
155
156 loop = asyncio.get_running_loop()
157
158 lookup = await loop.getaddrinfo(
82159 host=name,
83160 port=None,
84 family=socket.AF_INET6,
85 type=socket.SOCK_DGRAM
86 )[0][4][0]
161 family=_family,
162 type=socket.SOCK_DGRAM)
163
164 return [address[4][0] for address in lookup]
87165
88166 except OSError:
89 return name
167 if not family:
168 return await async_resolve(name, 6)
169
170 raise NameLookupError(name)
171
172
173 def is_hostname(name):
174 '''
175 Indicate whether the specified name is a hostname or an FQDN.
176 Return a `boolean`.
177
178 '''
179 pattern = r'(?i)^([a-z0-9-]+|([a-z0-9_-]+[.])+[a-z]+)$'
180 return match(pattern, name) is not None
90181
91182
92183 def is_ipv4_address(address):
95186 Return a `boolean`.
96187
97188 '''
98 return match(
99 r'^([0-9]{1,3}[.]){3}[0-9]{1,3}$',
100 address
101 ) is not None
189 pattern = r'^([0-9]{1,3}[.]){3}[0-9]{1,3}$'
190 return match(pattern, address) is not None
102191
103192
104193 def is_ipv6_address(address):
0 Metadata-Version: 2.1
1 Name: icmplib
2 Version: 3.0
3 Summary: Easily forge ICMP packets and make your own ping and traceroute.
4 Home-page: https://github.com/ValentinBELYN/icmplib
5 Author: Valentin BELYN
6 Author-email: [email protected]
7 License: GNU Lesser General Public License v3.0
8 Description: <div align="center">
9 <br>
10 <img src="media/icmplib-logo-2.0.png" height="190" width="100" alt="icmplib">
11 <br>
12 <br>
13
14 <br>
15 <div>
16 <a href="#features">Features</a>&nbsp;&nbsp;&nbsp;
17 <a href="#installation">Installation</a>&nbsp;&nbsp;&nbsp;
18 <a href="#built-in-functions">Built-in functions</a>&nbsp;&nbsp;&nbsp;
19 <a href="#icmp-sockets">ICMP sockets</a>&nbsp;&nbsp;&nbsp;
20 <a href="#faq">FAQ</a>&nbsp;&nbsp;&nbsp;
21 <a href="#contributing">Contributing</a>&nbsp;&nbsp;&nbsp;
22 <a href="#donate">Donate</a>&nbsp;&nbsp;&nbsp;
23 <a href="#license">License</a>
24 </div>
25 <br>
26
27 <pre>icmplib is a brand new and modern implementation of the ICMP protocol in Python.
28 Use the built-in functions or build your own, you have the choice!
29
30 <a href="#how-to-use-the-library-without-root-privileges">- You can now use this library without root privileges -</a></pre>
31 <a href="https://pypi.org/project/icmplib">
32 <img src="https://img.shields.io/pypi/dm/icmplib.svg?style=flat-square&labelColor=0366d6&color=005cc5" alt="statistics">
33 </a>
34 </div>
35
36 <br>
37
38 ## Features
39
40 - :deciduous_tree: **Ready-to-use:** icmplib offers ready-to-use functions such as the most popular ones: `ping`, `multiping` and `traceroute`.
41 - :gem: **Modern:** This library uses the latest technologies offered by Python 3.6+ and is fully object-oriented.
42 - :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!
43 - :zap: **Powerful:** Use the library without root privileges, set the traffic class of ICMP packets, customize their payload and more!
44 - :nut_and_bolt: **Evolutive:** Easily build your own classes and functions with `ICMPv4` and `ICMPv6` sockets.
45 - :fire: **Seamless integration of IPv6:** Use IPv6 the same way you use IPv4. Automatic detection is done without impacting performance.
46 - :rainbow: **Broadcast support** (you must use the `ICMPv4Socket` class to enable it).
47 - :beer: **Support of all operating systems.** Tested on Linux, macOS and Windows.
48 - :metal: **No dependency:** icmplib is a pure Python implementation of the ICMP protocol. It does not use any external dependencies.
49
50 <br>
51
52 ## Installation
53
54 The recommended way to install or upgrade icmplib is to use `pip3`:
55
56 ```shell
57 $ pip3 install icmplib
58 $ pip3 install --upgrade icmplib
59 ```
60
61 *icmplib requires Python 3.6 or later.*
62
63 To import icmplib into your project (only import what you need):
64
65 ```python
66 # For simple use
67 from icmplib import ping, multiping, traceroute, resolve, Host, Hop
68
69 # For advanced use (sockets)
70 from icmplib import ICMPv4Socket, ICMPv6Socket, ICMPRequest, ICMPReply
71
72 # Exceptions
73 from icmplib import ICMPLibError, NameLookupError, ICMPSocketError
74 from icmplib import SocketAddressError, SocketPermissionError
75 from icmplib import SocketUnavailableError, SocketBroadcastError, TimeoutExceeded
76 from icmplib import ICMPError, DestinationUnreachable, TimeExceeded
77 ```
78
79 <br>
80
81 ## Built-in functions
82
83 ### Ping
84 Send ICMP Echo Request packets to a network host.
85
86 ```python
87 ping(address, count=4, interval=1, timeout=2, id=PID, source=None, privileged=True, **kwargs)
88 ```
89
90 #### Parameters
91 - `address`
92
93 The IP address, hostname or FQDN of the host to which messages should be sent. For deterministic behavior, prefer to use an IP address.
94
95 - Type: `str`
96
97 - `count`
98
99 The number of ping to perform.
100
101 - Type: `int`
102 - Default: `4`
103
104 - `interval`
105
106 The interval in seconds between sending each packet.
107
108 - Type: `int` or `float`
109 - Default: `1`
110
111 - `timeout`
112
113 The maximum waiting time for receiving a reply in seconds.
114
115 - Type: `int` or `float`
116 - Default: `2`
117
118 - `id`
119
120 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.
121
122 - Type: `int`
123 - Default: `PID`
124
125 - `source`
126
127 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination.
128
129 - Type: `str`
130 - Default: `None`
131
132 - `privileged`
133
134 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.
135
136 *Only available on Unix systems. Ignored on Windows.*
137
138 - Type: `bool`
139 - Default: `True`
140
141 - `payload`
142
143 The payload content in bytes. A random payload is used by default.
144
145 - Type: `bytes`
146 - Default: `None`
147
148 - `payload_size`
149
150 The payload size. Ignored when the `payload` parameter is set.
151
152 - Type: `int`
153 - Default: `56`
154
155 - `traffic_class`
156
157 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.
158
159 *Only available on Unix systems. Ignored on Windows.*
160
161 - Type: `int`
162 - Default: `0`
163
164 #### Return value
165 - A `Host` object containing statistics about the desired destination:<br>
166 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`, `packets_received`, `packet_loss`, `is_alive`
167
168 #### Exceptions
169 - `NameLookupError`
170
171 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
172
173 - `SocketPermissionError`
174
175 If the privileges are insufficient to create the socket.
176
177 - `SocketAddressError`
178
179 If the source address cannot be assigned to the socket.
180
181 - `ICMPSocketError`
182
183 If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details.
184
185 #### Example
186 ```python
187 >>> host = ping('1.1.1.1', count=10, interval=0.2)
188
189 >>> host.address # The IP address of the host that responded
190 '1.1.1.1' # to the request
191
192 >>> host.min_rtt # The minimum round-trip time
193 12.2
194
195 >>> host.avg_rtt # The average round-trip time
196 13.2
197
198 >>> host.max_rtt # The maximum round-trip time
199 17.6
200
201 >>> host.packets_sent # The number of packets transmitted to the
202 10 # destination host
203
204 >>> host.packets_received # The number of packets sent by the remote
205 9 # host and received by the current host
206
207 >>> host.packet_loss # Packet loss occurs when packets fail to
208 0.1 # reach their destination. Returns a float
209 # between 0 and 1 (all packets are lost)
210 >>> host.is_alive # Indicates whether the host is reachable
211 True
212 ```
213
214 <br>
215
216 ### Multiping
217 Send ICMP Echo Request packets to several network hosts.
218
219 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.
220
221 ```python
222 multiping(addresses, count=2, interval=0.01, timeout=2, id=PID, source=None, privileged=True, **kwargs)
223 ```
224
225 #### Parameters
226 - `addresses`
227
228 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.
229
230 - Type: `list of str`
231
232 - `count`
233
234 The number of ping to perform per address.
235
236 - Type: `int`
237 - Default: `2`
238
239 - `interval`
240
241 The interval in seconds between sending each packet.
242
243 - Type: `int` or `float`
244 - Default: `0.01`
245
246 - `timeout`
247
248 The maximum waiting time for receiving all responses in seconds.
249
250 - Type: `int` or `float`
251 - Default: `2`
252
253 - `id`
254
255 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.
256
257 - Type: `int`
258 - Default: `PID`
259
260 - `source`
261
262 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.
263
264 - Type: `str`
265 - Default: `None`
266
267 - `privileged`
268
269 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.
270
271 *Only available on Unix systems. Ignored on Windows.*
272
273 - Type: `bool`
274 - Default: `True`
275
276 - `payload`
277
278 The payload content in bytes. A random payload is used by default.
279
280 - Type: `bytes`
281 - Default: `None`
282
283 - `payload_size`
284
285 The payload size. Ignored when the `payload` parameter is set.
286
287 - Type: `int`
288 - Default: `56`
289
290 - `traffic_class`
291
292 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.
293
294 *Only available on Unix systems. Ignored on Windows.*
295
296 - Type: `int`
297 - Default: `0`
298
299 #### Return value
300 - A `list of Host` objects containing statistics about the desired destinations:<br>
301 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`, `packets_received`, `packet_loss`, `is_alive`
302
303 The list is sorted in the same order as the addresses passed in parameters.
304
305 #### Exceptions
306 - `SocketPermissionError`
307
308 If the privileges are insufficient to create the socket.
309
310 - `SocketAddressError`
311
312 If the source address cannot be assigned to the socket.
313
314 - `ICMPSocketError`
315
316 If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details.
317
318 #### Example
319 ```python
320 >>> hosts = multiping(['10.0.0.5', '127.0.0.1', '::1'])
321
322 >>> for host in hosts:
323 ... if host.is_alive:
324 ... # See the Host class for details
325 ... print(f'{host.address} is alive!')
326 ...
327 ... else:
328 ... print(f'{host.address} is dead!')
329 ...
330
331 # 10.0.0.5 is dead!
332 # 127.0.0.1 is alive!
333 # ::1 is alive!
334 ```
335
336 <br>
337
338 ### Traceroute
339 Determine the route to a destination host.
340
341 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.
342
343 *This function requires root privileges to run.*
344
345 ```python
346 traceroute(address, count=2, interval=0.05, timeout=2, id=PID, first_hop=1, max_hops=30, source=None, fast=False, **kwargs)
347 ```
348
349 #### Parameters
350 - `address`
351
352 The IP address, hostname or FQDN of the host to reach. For deterministic behavior, prefer to use an IP address.
353
354 - Type: `str`
355
356 - `count`
357
358 The number of ping to perform per hop.
359
360 - Type: `int`
361 - Default: `2`
362
363 - `interval`
364
365 The interval in seconds between sending each packet.
366
367 - Type: `int` or `float`
368 - Default: `0.05`
369
370 - `timeout`
371
372 The maximum waiting time for receiving a reply in seconds.
373
374 - Type: `int` or `float`
375 - Default: `2`
376
377 - `id`
378
379 The identifier of ICMP requests. Used to match the responses with requests. In practice, a unique identifier should be used for every traceroute process.
380
381 - Type: `int`
382 - Default: `PID`
383
384 - `first_hop`
385
386 The initial time to live value used in outgoing probe packets.
387
388 - Type: `int`
389 - Default: `1`
390
391 - `max_hops`
392
393 The maximum time to live (max number of hops) used in outgoing probe packets.
394
395 - Type: `int`
396 - Default: `30`
397
398 - `source`
399
400 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination.
401
402 - Type: `str`
403 - Default: `None`
404
405 - `fast`
406
407 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.
408
409 - Type: `bool`
410 - Default: `False`
411
412 - `payload`
413
414 The payload content in bytes. A random payload is used by default.
415
416 - Type: `bytes`
417 - Default: `None`
418
419 - `payload_size`
420
421 The payload size. Ignored when the `payload` parameter is set.
422
423 - Type: `int`
424 - Default: `56`
425
426 - `traffic_class`
427
428 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.
429
430 *Only available on Unix systems. Ignored on Windows.*
431
432 - Type: `int`
433 - Default: `0`
434
435 #### Return value
436 - 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`.
437
438 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.
439
440 #### Exceptions
441 - `NameLookupError`
442
443 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
444
445 - `SocketPermissionError`
446
447 If the privileges are insufficient to create the socket.
448
449 - `SocketAddressError`
450
451 If the source address cannot be assigned to the socket.
452
453 - `ICMPSocketError`
454
455 If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details.
456
457 #### Example
458 ```python
459 >>> hops = traceroute('1.1.1.1')
460
461 >>> print('Distance/TTL Address Average round-trip time')
462 >>> last_distance = 0
463
464 >>> for hop in hops:
465 ... if last_distance + 1 != hop.distance:
466 ... print('Some gateways are not responding')
467 ...
468 ... # See the Hop class for details
469 ... print(f'{hop.distance} {hop.address} {hop.avg_rtt} ms')
470 ...
471 ... last_distance = hop.distance
472 ...
473
474 # Distance/TTL Address Average round-trip time
475 # 1 10.0.0.1 5.196 ms
476 # 2 194.149.169.49 7.552 ms
477 # 3 194.149.166.54 12.21 ms
478 # * Some gateways are not responding
479 # 5 212.73.205.22 22.15 ms
480 # 6 1.1.1.1 13.59 ms
481 ```
482
483 <br>
484
485 ## ICMP sockets
486
487 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.
488
489 ```
490 ┌──────────────────┐
491 ┌─────────────────┐ send(...) │ ICMPv4Socket │ receive() ┌─────────────────┐
492 │ ICMPRequest │ ────────────> │ or │ ────────────> │ ICMPReply │
493 └─────────────────┘ │ ICMPv6Socket │ └─────────────────┘
494 └──────────────────┘
495 ```
496
497 ### ICMPRequest
498 A user-created object that represents an ICMP Echo Request.
499
500 ```python
501 ICMPRequest(destination, id, sequence, payload=None, payload_size=56, ttl=64, traffic_class=0)
502 ```
503
504 #### Parameters and properties
505 - `destination`
506
507 The IP address of the host to which the message should be sent.
508
509 - Type: `str`
510
511 - `id`
512
513 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.
514
515 - Type: `int`
516
517 - `sequence`
518
519 The sequence number. Used to match the reply with the request. Typically, the sequence number is incremented for each packet sent during the process.
520
521 - Type: `int`
522
523 - `payload`
524
525 The payload content in bytes. A random payload is used by default.
526
527 - Type: `bytes`
528 - Default: `None`
529
530 - `payload_size`
531
532 The payload size. Ignored when the `payload` parameter is set.
533
534 - Type: `int`
535 - Default: `56`
536
537 - `ttl`
538
539 The time to live of the packet in terms of hops.
540
541 - Type: `int`
542 - Default: `64`
543
544 - `traffic_class`
545
546 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.
547
548 *Only available on Unix systems. Ignored on Windows.*
549
550 - Type: `int`
551 - Default: `0`
552
553 #### Properties only
554 - `time`
555
556 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.
557
558 - Type: `float`
559
560 <br>
561
562 ### ICMPReply
563 A class that represents an ICMP reply. Generated from an ICMP socket (`ICMPv4Socket` or `ICMPv6Socket`).
564
565 ```python
566 ICMPReply(source, id, sequence, type, code, bytes_received, time)
567 ```
568
569 #### Parameters and properties
570 - `source`
571
572 The IP address of the gateway or host that composes the ICMP message.
573
574 - Type: `str`
575
576 - `id`
577
578 The identifier of the reply. Used to match the reply with the request.
579
580 - Type: `int`
581
582 - `sequence`
583
584 The sequence number. Used to match the reply with the request.
585
586 - Type: `int`
587
588 - `type`
589
590 The type of ICMP message.
591
592 - Type: `int`
593
594 - `code`
595
596 The ICMP error code.
597
598 - Type: `int`
599
600 - `bytes_received`
601
602 The number of bytes received.
603
604 - Type: `int`
605
606 - `time`
607
608 The timestamp of the ICMP reply.
609
610 - Type: `float`
611
612 #### Methods
613 - `raise_for_status()`
614
615 Throw an exception if the reply is not an ICMP Echo Reply. Otherwise, do nothing.
616
617 - Raises `DestinationUnreachable`: If the destination is unreachable for some reason.
618 - Raises `TimeExceeded`: If the time to live field of the ICMP request has reached zero.
619 - Raises `ICMPError`: Raised for any other type and ICMP error code, except ICMP Echo Reply messages.
620
621 <br>
622
623 ### ICMPv4Socket
624 Class for sending and receiving ICMPv4 packets.
625
626 ```python
627 ICMPv4Socket(address=None, privileged=True)
628 ```
629
630 #### Parameters
631 - `source`
632
633 The IP address from which you want to listen and send packets. By default, the socket listens on all interfaces.
634
635 - Type: `str`
636 - Default: `None`
637
638 - `privileged`
639
640 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.
641
642 *Only available on Unix systems. Ignored on Windows.*
643
644 - Type: `bool`
645 - Default: `True`
646
647 #### Methods
648 - `__init__(address=None, privileged=True)`
649
650 *Constructor. Automatically called: do not call it directly.*
651
652 - Raises `SocketPermissionError`: If the privileges are insufficient to create the socket.
653 - Raises `SocketAddressError`: If the requested address cannot be assigned to the socket.
654 - Raises `ICMPSocketError`: If another error occurs while creating the socket.
655
656 - `__del__()`
657
658 *Destructor. Automatically called: do not call it directly.*
659
660 Call the `close` method.
661
662 - `send(request)`
663
664 Send an ICMP request message over the network to a remote host.<br>
665 This operation is non-blocking. Use the `receive` method to get the reply.
666
667 - 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.
668 - Raises `SocketBroadcastError`: If a broadcast address is used and the corresponding option is not enabled on the socket (ICMPv4 only).
669 - Raises `SocketUnavailableError`: If the socket is closed.
670 - Raises `ICMPSocketError`: If another error occurs while sending.
671
672 - `receive(request=None, timeout=2)`
673
674 Receive an ICMP reply message from the socket.<br>
675 This method can be called multiple times if you expect several responses as with a broadcast address.
676
677 - Parameter `request` *(ICMPRequest)*: The ICMP request to use to match the response. By default, all ICMP packets arriving on the socket are returned.
678 - Parameter `timeout` *(int or float)*: The maximum waiting time for receiving the response in seconds. Default to `2`.
679 - Raises `TimeoutExceeded`: If no response is received before the timeout specified in parameters.
680 - Raises `SocketUnavailableError`: If the socket is closed.
681 - Raises `ICMPSocketError`: If another error occurs while receiving.
682
683 Returns an `ICMPReply` object representing the response of the desired destination or an upstream gateway.
684
685 - `close()`
686
687 Close the socket. It cannot be used after this call.
688
689 #### Properties
690 - `address`
691
692 The IP address from which the socket listens and sends packets. Return `None` if the socket listens on all interfaces.
693
694 - Type: `str`
695
696 - `is_privileged`
697
698 Indicate whether the socket is running in privileged mode.
699
700 - Type: `bool`
701
702 - `is_closed`
703
704 Indicate whether the socket is closed.
705
706 - Type: `bool`
707
708 #### Properties and setters
709 - `broadcast`
710
711 Enable or disable the broadcast support on the socket.
712
713 - Type: `bool`
714 - Default: `False`
715
716 <br>
717
718 ### ICMPv6Socket
719 Class for sending and receiving ICMPv6 packets.
720
721 ```python
722 ICMPv6Socket(address=None, privileged=True)
723 ```
724
725 #### Methods and properties
726 The same methods and properties as for the `ICMPv4Socket` class, except the `broadcast` property.
727
728 <br>
729
730 ### Exceptions
731 The library contains many exceptions to adapt to your needs:
732
733 ```
734 ICMPLibError
735 ├─ NameLookupError
736 ├─ ICMPSocketError
737 │ ├─ SocketAddressError
738 │ ├─ SocketPermissionError
739 │ ├─ SocketUnavailableError
740 │ ├─ SocketBroadcastError
741 │ └─ TimeoutExceeded
742
743 └─ ICMPError
744 ├─ DestinationUnreachable
745 │ ├─ ICMPv4DestinationUnreachable
746 │ └─ ICMPv6DestinationUnreachable
747
748 └─ TimeExceeded
749 ├─ ICMPv4TimeExceeded
750 └─ ICMPv6TimeExceeded
751 ```
752
753 - `ICMPLibError`: Exception class for the icmplib package.
754 - `NameLookupError`: Raised when the requested name does not exist or cannot be resolved. This concerns both Fully Qualified Domain Names and hostnames.
755 - `ICMPSocketError`: Base class for ICMP sockets exceptions.
756 - `SocketAddressError`: Raised when the requested address cannot be assigned to the socket.
757 - `SocketPermissionError`: Raised when the privileges are insufficient to create the socket.
758 - `SocketUnavailableError`: Raised when an action is performed while the socket is closed.
759 - `SocketBroadcastError`: Raised when a broadcast address is used and the corresponding option is not enabled on the socket.
760 - `TimeoutExceeded`: Raised when a timeout occurs on a socket.
761 - `ICMPError`: Base class for ICMP error messages.
762 - `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.
763 - `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.
764
765 Use the `message` property to get the error message. `ICMPError` subclasses have a `reply` property to retrieve the response.
766
767 <br>
768
769 ### Examples
770 #### Sockets in action
771 ```python
772 def single_ping(address, timeout=2, id=PID):
773 # Create an ICMP socket
774 sock = ICMPv4Socket()
775
776 # Create an ICMP request
777 # See the 'ICMPRequest' class for details
778 request = ICMPRequest(
779 destination=address,
780 id=id,
781 sequence=1)
782
783 try:
784 sock.send(request)
785
786 # If the program arrives in this section, it means that the
787 # packet has been transmitted.
788
789 reply = sock.receive(request, timeout)
790
791 # If the program arrives in this section, it means that a
792 # packet has been received. The reply has the same identifier
793 # and sequence number that the request but it can come from
794 # an intermediate gateway.
795
796 reply.raise_for_status()
797
798 # If the program arrives in this section, it means that the
799 # destination host has responded to the request.
800
801 except TimeoutExceeded as err:
802 # The timeout has been reached
803 print(err)
804
805 except DestinationUnreachable as err:
806 # The reply indicates that the destination host is unreachable
807 print(err)
808
809 # Retrieve the response
810 reply = err.reply
811
812 except TimeExceeded as err:
813 # The reply indicates that the time to live exceeded in transit
814 print(err)
815
816 # Retrieve the response
817 reply = err.reply
818
819 except ICMPLibError as err:
820 # All other errors
821 print(err)
822
823 # Automatic socket closure (garbage collector)
824 ```
825
826 #### Verbose ping
827 ```python
828 def verbose_ping(address, count=4, interval=1, timeout=2, id=PID):
829 # A payload of 56 bytes is used by default. You can modify it using
830 # the 'payload_size' parameter of your ICMP request.
831 print(f'PING {address}: 56 data bytes\n')
832
833 # We detect the socket to use from the specified IP address
834 if is_ipv6_address(address):
835 sock = ICMPv6Socket()
836
837 else:
838 sock = ICMPv4Socket()
839
840 for sequence in range(count):
841 # We create an ICMP request
842 request = ICMPRequest(
843 destination=address,
844 id=id,
845 sequence=sequence)
846
847 try:
848 # We send the request
849 sock.send(request)
850
851 # We are awaiting receipt of an ICMP reply
852 reply = sock.receive(request, timeout)
853
854 # We received a reply
855 # We display some information
856 print(f'{reply.bytes_received} bytes from '
857 f'{reply.source}: ', end='')
858
859 # We throw an exception if it is an ICMP error message
860 reply.raise_for_status()
861
862 # We calculate the round-trip time and we display it
863 round_trip_time = (reply.time - request.time) * 1000
864
865 print(f'icmp_seq={sequence} '
866 f'time={round(round_trip_time, 3)} ms')
867
868 # We pause before continuing
869 if sequence < count - 1:
870 sleep(interval)
871
872 except TimeoutExceeded:
873 # The timeout has been reached
874 print(f'Request timeout for icmp_seq {sequence}')
875
876 except ICMPError as err:
877 # An ICMP error message has been received
878 print(err)
879
880 except ICMPLibError:
881 # All other errors
882 print('An error has occurred.')
883
884
885 verbose_ping('1.1.1.1')
886
887 # PING 1.1.1.1: 56 data bytes
888 # 64 bytes from 1.1.1.1: icmp_seq=0 time=12.061 ms
889 # 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms
890 # 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms
891 # 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms
892 ```
893
894 <br>
895
896 ## FAQ
897
898 ### How to resolve a FQDN/domain name or a hostname?
899 The use of the built-in `resolve` function is recommended:
900
901 ```python
902 >>> resolve('github.com')
903 '140.82.118.4'
904 ```
905
906 - If several IP addresses are available, only the first one is returned. This function searches for IPv4 addresses first before searching for IPv6 addresses.
907 - If you pass an IP address, no lookup is done. The same address is returned.
908 - Raises a `NameLookupError` exception if the requested name does not exist or cannot be resolved.
909
910 ### How to use the library without root privileges?
911 Since its version 2.0, icmplib can be used without root privileges.
912
913 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.
914
915 On some Linux systems, you must allow this feature:
916
917 ```shell
918 $ echo 'net.ipv4.ping_group_range = 0 2147483647' | sudo tee -a /etc/sysctl.conf
919 $ sudo sysctl -p
920 ```
921
922 You can check the current value with the following command:
923
924 ```shell
925 $ sysctl net.ipv4.ping_group_range
926 net.ipv4.ping_group_range = 0 2147483647
927 ```
928
929 *Since Ubuntu 20.04 LTS, this manipulation is no longer necessary.*
930
931 [Read more on www.kernel.org](https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt)
932
933 ### Why I have no response from a remote host?
934 In the event of no response from a remote host, several causes are possible:
935 - 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.
936 - The remote host or an upstream gateway is down.
937 - The remote host or an upstream gateway drops ICMP messages for security reasons.
938 - 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.
939
940 <br>
941
942 ## Contributing
943
944 Comments and enhancements are welcome.
945
946 All development is done on [GitHub](https://github.com/ValentinBELYN/icmplib). Use [Issues](https://github.com/ValentinBELYN/icmplib/issues) to report problems and submit feature requests. Please include a minimal example that reproduces the bug.
947
948 ## Donate
949
950 icmplib is completely free and open source. It has been fully developed on my free time. If you enjoy it, please consider donating to support the development.
951
952 - [:tada: Donate via PayPal](https://paypal.me/ValentinBELYN)
953
954 ## License
955
956 Copyright 2017-2021 Valentin BELYN.
957
958 Code released under the GNU LGPLv3 license. See the [LICENSE](LICENSE) for details.
959
960 Keywords: pure,implementation,icmp,sockets,ping,multiping,traceroute,ipv4,ipv6,python3
961 Platform: UNKNOWN
962 Classifier: Development Status :: 5 - Production/Stable
963 Classifier: Intended Audience :: Developers
964 Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
965 Classifier: Operating System :: OS Independent
966 Classifier: Programming Language :: Python
967 Classifier: Programming Language :: Python :: 3
968 Classifier: Programming Language :: Python :: 3 :: Only
969 Classifier: Programming Language :: Python :: 3.7
970 Classifier: Programming Language :: Python :: 3.8
971 Classifier: Programming Language :: Python :: 3.9
972 Classifier: Programming Language :: Python :: 3.10
973 Classifier: Topic :: Software Development :: Libraries
974 Classifier: Topic :: Software Development :: Libraries :: Python Modules
975 Requires-Python: >=3.7
976 Description-Content-Type: text/markdown
0 LICENSE
1 README.md
2 setup.cfg
3 setup.py
4 icmplib/__init__.py
5 icmplib/exceptions.py
6 icmplib/models.py
7 icmplib/multiping.py
8 icmplib/ping.py
9 icmplib/sockets.py
10 icmplib/traceroute.py
11 icmplib/utils.py
12 icmplib.egg-info/PKG-INFO
13 icmplib.egg-info/SOURCES.txt
14 icmplib.egg-info/dependency_links.txt
15 icmplib.egg-info/top_level.txt
media/icmplib-logo.png less more
Binary diff not shown
00 [metadata]
1 name = icmplib
2 version = 1.2.2
3 description = Easily forge ICMP packets and make your own ping and traceroute.
4 keywords = pure, implementation, icmp, sockets, ping, multiping, traceroute, ipv4, ipv6, python3
5
6 author = Valentin BELYN
7 author_email = [email protected]
8
9 long_description = file: README.md
10 long_description_content_type = text/markdown
11 url = https://github.com/ValentinBELYN/icmplib
12
13 license = GNU Lesser General Public License v3.0
14 license_file = LICENSE
15
16 classifiers =
17 Development Status :: 5 - Production/Stable
18 Intended Audience :: Developers
19 License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
20 Operating System :: OS Independent
21 Programming Language :: Python :: 3
22 Programming Language :: Python :: 3 :: Only
23 Programming Language :: Python :: 3.6
24 Programming Language :: Python :: 3.7
25 Programming Language :: Python :: 3.8
26 Topic :: Software Development :: Libraries
27 Topic :: Software Development :: Libraries :: Python Modules
1 name = icmplib
2 version = 3.0
3 description = Easily forge ICMP packets and make your own ping and traceroute.
4 keywords = pure, implementation, icmp, sockets, ping, multiping, traceroute, ipv4, ipv6, python3
5 author = Valentin BELYN
6 author_email = [email protected]
7 long_description = file: README.md
8 long_description_content_type = text/markdown
9 url = https://github.com/ValentinBELYN/icmplib
10 license = GNU Lesser General Public License v3.0
11 license_file = LICENSE
12 classifiers =
13 Development Status :: 5 - Production/Stable
14 Intended Audience :: Developers
15 License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
16 Operating System :: OS Independent
17 Programming Language :: Python
18 Programming Language :: Python :: 3
19 Programming Language :: Python :: 3 :: Only
20 Programming Language :: Python :: 3.7
21 Programming Language :: Python :: 3.8
22 Programming Language :: Python :: 3.9
23 Programming Language :: Python :: 3.10
24 Topic :: Software Development :: Libraries
25 Topic :: Software Development :: Libraries :: Python Modules
2826
2927 [options]
30 packages = icmplib
31 python_requires = >=3.6
28 packages = icmplib
29 python_requires = >=3.7
30
31 [egg_info]
32 tag_build =
33 tag_date = 0
34
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 ~~~~~~~