Repository navigation
Expand file tree
/
Copy pathICMP_Pinger.py
More file actions
129 lines (107 loc) · 4.15 KB
/
Copy pathICMP_Pinger.py
File metadata and controls
129 lines (107 loc) · 4.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import os
import sys
import struct
import time
import select
import socket
ICMP_ECHO_REQUEST = 8
def checksum(data):
csum = 0
countTo = (len(data) // 2) * 2
count = 0
while count < countTo:
thisVal = data[count + 1] * 256 + data[count]
csum += thisVal
csum &= 0xFFFFFFFF
count += 2
if countTo < len(data):
csum += data[len(data) - 1]
csum &= 0xFFFFFFFF
csum = (csum >> 16) + (csum & 0xFFFF)
csum += (csum >> 16)
answer = ~csum & 0xFFFF
answer = answer >> 8 | (answer << 8 & 0xFF00)
return answer
def receiveOnePing(mySocket, ID, timeout, destAddr):
global rtt_min, rtt_max, rtt_sum, rtt_cnt
timeLeft = timeout
while True:
startedSelect = time.time()
whatReady = select.select([mySocket], [], [], timeLeft)
howLongInSelect = (time.time() - startedSelect)
if whatReady[0] == []: # Timeout
return "Request timed out."
timeReceived = time.time()
recPacket, addr = mySocket.recvfrom(1024)
# Fetch the ICMP header from the IP packet
icmpHeader = struct.unpack('bbHHh', recPacket[20:28])
type, code, checksum, packetID, sequence = icmpHeader
if type != 0:
return f"Expected type=0, but got {type}"
if code != 0:
return f"Expected code=0, but got {code}"
if ID != packetID:
return f"Expected ID={ID}, but got {packetID}"
send_time, = struct.unpack('d', recPacket[28:])
rtt = (timeReceived - send_time) * 1000
rtt_cnt += 1
rtt_sum += rtt
rtt_min = min(rtt_min, rtt)
rtt_max = max(rtt_max, rtt)
ipHeader = struct.unpack('!BBHHHBBH4s4s', recPacket[:20])
ttl = ipHeader[5]
saddr = socket.inet_ntoa(ipHeader[8])
length = len(recPacket) - 20
return f"{length} bytes from {saddr}: icmp_seq={sequence} ttl={ttl} time={rtt:.3f} ms"
timeLeft -= howLongInSelect
if timeLeft <= 0:
return "Request timed out."
def sendOnePing(mySocket, destAddr, ID):
# Header is type (8), code (8), checksum (16), id (16), sequence (16)
myChecksum = 0
# Make a dummy header with a 0 checksum.
header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, myChecksum, ID, 1)
data = struct.pack("d", time.time())
# Calculate the checksum on the data and the dummy header.
myChecksum = checksum(header + data)
# Get the right checksum, and put in the header
myChecksum = socket.htons(myChecksum)
header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, myChecksum, ID, 1)
packet = header + data
mySocket.sendto(packet, (destAddr, 1)) # AF_INET address must be tuple, not str
def doOnePing(destAddr, timeout):
icmp = socket.getprotobyname("icmp")
# Create a raw socket
mySocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
myID = os.getpid() & 0xFFFF # Return the current process ID
sendOnePing(mySocket, destAddr, myID)
delay = receiveOnePing(mySocket, myID, timeout, destAddr)
mySocket.close()
return delay
def ping(host, timeout=1):
global rtt_min, rtt_max, rtt_sum, rtt_cnt
rtt_min = float('+inf')
rtt_max = float('-inf')
rtt_sum = 0
rtt_cnt = 0
cnt = 0
if host.startswith("http://") or host.startswith("https://"):
host = host.split("//")[1]
dest = socket.gethostbyname(host)
print(f"Pinging {dest} using Python:")
try:
while True:
cnt += 1
print(doOnePing(dest, timeout))
time.sleep(1)
except KeyboardInterrupt:
if cnt != 0:
print(f"--- {host} ping statistics ---")
print(f"{cnt} packets transmitted, {rtt_cnt} packets received, {100.0 - rtt_cnt * 100.0 / cnt:.1f}% packet loss")
if rtt_cnt != 0:
print(f"round-trip min/avg/max {rtt_min:.3f}/{rtt_sum / rtt_cnt:.3f}/{rtt_max:.3f} ms")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python ICMP_Pinger.py <hostname>")
else:
ping(sys.argv[1])