-
Notifications
You must be signed in to change notification settings - Fork 0
/
icmp_ping.py
executable file
·244 lines (201 loc) · 9.45 KB
/
icmp_ping.py
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#!/usr/bin/env python3
# coding: utf-8
# ICMPを送信する
# Copyright (c) 2023 Wataru KUNINO
# ICMPを送信します。
# sudo ./icmp_ping.py
# 第1引数は宛先IPアドレスです。
# sudo ./icmp_ping.py 127.0.0.1
# 第2引数以降は送信データです。
# sudo ./icmp_ping.py 127.0.0.1 data1 data2...
import sys
import socket
import ipaddress
from time import time
from random import randint
adr = '127.0.0.1'
icm_type = b'\x08' # header[0] Type = echo message
icm_code = b'\x00' # header[1] Code = 0
icm_csum = b'\x00\x00' # header[2:4] Checksum = 0x0000 計算前の初期値
icm_idnt = b'\x00\x04' # header[4:6] Identifier
icm_snum = b'\x00\x01' # header[6:8] Sequence Number
def checksum_calc(payload):
if len(payload)%2 == 1:
payload += b'\x00' # total length is odd, padded with one octet of zeros
sum = 0x0000
for i in range(len(payload)//2): # 1 の補数和
sum += int(payload[i*2]) * 256
sum += int(payload[i*2+1])
if sum > 0xFFFF:
sum += 1
sum &= 0xFFFF
sum = ~(sum) & 0xFFFF
return sum.to_bytes(2, 'big')
argc = len(sys.argv) # 引数の数をargcへ代入
print('ICMP Ping Sender Receiver') # タイトル表示
print('Usage: sudo',sys.argv[0],'[ip_address] [data...]') # 使用方法
if argc >= 2: # 入力パラメータ数の確認
try:
ipaddress.ip_interface(sys.argv[1])
adr = sys.argv[1] # IPアドレスを設定
del sys.argv[1]
except ValueError:
pass
body = ''
if argc >= 2:
for word in sys.argv[1:]:
if len(body) > 0:
body += ' '
body += word
if len(body) > 0:
print('send Ping "'+body+'" to',adr)
else:
print('send Ping to',adr)
# Header の生成
icm_idnt = randint(0,65535).to_bytes(2, 'big') # Identifier = 乱数
icm_snum = (int(time()) % 65536).to_bytes(2, 'big') # Sequence Number = 秒数
header = icm_type + icm_code + icm_csum + icm_idnt + icm_snum
# Checksum の計算
payload = header + body.encode()
checksum = checksum_calc(payload)
# Header にChecksumを付与
header = icm_type + icm_code + checksum + icm_idnt + icm_snum
payload = header + body.encode()
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
except PermissionError: # 例外処理発生時
print('PermissionError, 先頭に sudo を付与して実行してください')
exit() # プログラムの終了
if sock: # 作成に成功したとき
print('ICMP TX('+('{:02x}'.format(len(payload)))+')',end=' : ')
for c in payload:
print('{:02x}'.format(c), end=' ') # 受信データを表示
print()
sock.sendto(payload,(adr,0)) # Ping送信
sock.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1) #
sock.settimeout(1)
while sock:
try:
icmp = sock.recv(256) # 受信データの取得
except socket.timeout:
break
length = int(256 * icmp[2] + icmp[3])
header_length = int((icmp[0]%16)*4)
protocol = icmp[9]
source_adr = int.from_bytes(icmp[12:16], 'big')
source_adr_s = str(ipaddress.IPv4Address(source_adr))
dest_adr = int.from_bytes(icmp[16:20], 'big')
dest_adr_s = str(ipaddress.IPv4Address(dest_adr))
if icmp[0] == 0x45 and protocol == 0x01 and length == len(icmp) and icmp[20] == 0x00:
icmp_len = length - header_length
identifier = int.from_bytes(icmp[24:26], 'big')
sequence = int.from_bytes(icmp[26:28], 'big')
check = not int.from_bytes(checksum_calc(icmp[20:]),'big')
if identifier == int.from_bytes(icm_idnt,'big') and sequence == int.from_bytes(icm_snum,'big') and check:
# icmp[0] == 0x45: IPv4と、IPヘッダ長20バイトに限定
print('ICMP RX('+'{:02x}'.format(icmp_len)+')',end=' : ')
for i in range(len(icmp)-len(payload),len(icmp)):
print('{:02x}'.format(icmp[i]), end=' ') # 受信データを表示
print()
print('IP Version =','v'+str(int(icmp[0]>>4)))
print('IP Header =',header_length)
print('IP Length =',length)
print('Protocol =','0x'+('{:02x}'.format(protocol)))
print('Source =',source_adr_s)
print('Destination =',dest_adr_s)
print('ICMP Length =',icmp_len)
print('ICMP Type =','{:02x}'.format(icmp[20]))
print('ICMP Code =','{:02x}'.format(icmp[21]))
print('Checksum =', 'Passed' if check else 'Failed')
print('Identifier =','{:04x}'.format(identifier))
print('Sequence N =','{:04x}'.format(sequence))
if icmp_len > 8:
print('ICMP Data =',icmp[28:].decode())
sock.close() # ソケットの切断
###############################################################################
# 参考文献 RAWソケットを利用したpingコマンド (Geekなページ)
'''
https://www.geekpage.jp/programming/linux-network/book/12/12-1.php
'''
###############################################################################
# 参考文献 TCP/IP - ICMPとは (ネットワークエンジニアとして)
'''
https://www.infraexpert.com/study/tcpip4.html
'''
###############################################################################
# 参考文献 INTERNET CONTROL MESSAGE PROTOCOL (IETF RFC 792)
'''
https://www.rfc-editor.org/rfc/rfc792
Echo or Echo Reply Message
0 1 2 3
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identifier | Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ...
+-+-+-+-+-
IP Fields:
Addresses
The address of the source in an echo message will be the
destination of the echo reply message. To form an echo reply
message, the source and destination addresses are simply reversed,
the type code changed to 0, and the checksum recomputed.
IP Fields:
Type
8 for echo message;
0 for echo reply message.
Code
0
Checksum
The checksum is the 16-bit ones's complement of the one's
complement sum of the ICMP message starting with the ICMP Type.
For computing the checksum , the checksum field should be zero.
If the total length is odd, the received data is padded with one
octet of zeros for computing the checksum. This checksum may be
replaced in the future.
Identifier
If code = 0, an identifier to aid in matching echos and replies,
may be zero.
Sequence Number
If code = 0, a sequence number to aid in matching echos and
replies, may be zero.
Description
The data received in the echo message must be returned in the echo
reply message.
The identifier and sequence number may be used by the echo sender
to aid in matching the replies with the echo requests. For
example, the identifier might be used like a port in TCP or UDP to
identify a session, and the sequence number might be incremented
on each echo request sent. The echoer returns these same values
in the echo reply.
Code 0 may be received from a gateway or a host.
'''
###############################################################################
# 参考文献 INTERNET PROTOCOL (IETF RFC 791)
'''
https://www.rfc-editor.org/rfc/rfc791
3.1. Internet Header Format
A summary of the contents of the internet header follows:
0 1 2 3
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
'''
###############################################################################
# 参考文献 python raw socket: Protocol not supported (stackoverflow)
'''
https://stackoverflow.com/questions/19732145/python-raw-socket-protocol-not-supported
'''