Coverage for /root/GitHubProjects/impacket/impacket/ICMP6.py: 55%

# Coverage for /root/GitHubProjects/impacket/impacket/ICMP6.py : 55%

## 307 statements

Hot-keys on this page

r m x p toggle line displays

j k next/prev highlighted chunk

0 (zero) top of page

1 (one) first highlighted chunk

1# Impacket - Collection of Python classes for working with network protocols.

2#

3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved.

4#

5# This software is provided under a slightly modified version

6# of the Apache Software License. See the accompanying LICENSE file

7# for more information.

8#

9

10 import array

11 import struct

12

13 from impacket.ImpactPacket import Header, Data, array_tobytes

14 from impacket.IP6_Address import IP6_Address

15

16

17 class ICMP6(Header):

18 #IP Protocol number for ICMP6

19 IP_PROTOCOL_NUMBER = 58

20 protocol = IP_PROTOCOL_NUMBER #ImpactDecoder uses the constant "protocol" as the IP Protocol Number

21

22 #Size of ICMP6 header (excluding payload)

23 HEADER_SIZE = 4

24

25 #ICMP6 Message Type numbers

26 DESTINATION_UNREACHABLE = 1

27 PACKET_TOO_BIG = 2

28 TIME_EXCEEDED = 3

29 PARAMETER_PROBLEM = 4

30 ECHO_REQUEST = 128

31 ECHO_REPLY = 129

32 ROUTER_SOLICITATION = 133

33 ROUTER_ADVERTISEMENT = 134

34 NEIGHBOR_SOLICITATION = 135

35 NEIGHBOR_ADVERTISEMENT = 136

36 REDIRECT_MESSAGE = 137

37 NODE_INFORMATION_QUERY = 139

38 NODE_INFORMATION_REPLY = 140

39

40 #Destination Unreachable codes

41 NO_ROUTE_TO_DESTINATION = 0

42 ADMINISTRATIVELY_PROHIBITED = 1

43 BEYOND_SCOPE_OF_SOURCE_ADDRESS = 2

44 ADDRESS_UNREACHABLE = 3

45 PORT_UNREACHABLE = 4

46 SOURCE_ADDRESS_FAILED_INGRESS_EGRESS_POLICY = 5

47 REJECT_ROUTE_TO_DESTINATION = 6

48

49 #Time Exceeded codes

50 HOP_LIMIT_EXCEEDED_IN_TRANSIT = 0

51 FRAGMENT_REASSEMBLY_TIME_EXCEEDED = 1

52

53 #Parameter problem codes

54 ERRONEOUS_HEADER_FIELD_ENCOUNTERED = 0

55 UNRECOGNIZED_NEXT_HEADER_TYPE_ENCOUNTERED = 1

56 UNRECOGNIZED_IPV6_OPTION_ENCOUNTERED = 2

57

58 #Node Information codes

59 NODE_INFORMATION_QUERY_IPV6 = 0

60 NODE_INFORMATION_QUERY_NAME_OR_EMPTY = 1

61 NODE_INFORMATION_QUERY_IPV4 = 2

62 NODE_INFORMATION_REPLY_SUCCESS = 0

63 NODE_INFORMATION_REPLY_REFUSED = 1

64 NODE_INFORMATION_REPLY_UNKNOWN_QTYPE = 2

65

66 #Node Information qtypes

67 NODE_INFORMATION_QTYPE_NOOP = 0

68 NODE_INFORMATION_QTYPE_UNUSED = 1

69 NODE_INFORMATION_QTYPE_NODENAME = 2

70 NODE_INFORMATION_QTYPE_NODEADDRS = 3

71 NODE_INFORMATION_QTYPE_IPv4ADDRS = 4

72

73 #ICMP Message semantic types (error or informational)

74 ERROR_MESSAGE = 0

75 INFORMATIONAL_MESSAGE = 1

76

77 #ICMP message dictionary - specifying text descriptions and valid message codes

78 #Key: ICMP message number

79 #Data: Tuple ( Message Type (error/informational), Text description, Codes dictionary (can be None) )

80 #Codes dictionary

81 #Key: Code number

82 #Data: Text description

83

84 #ICMP message dictionary tuple indexes

85 MSG_TYPE_INDEX = 0

86 DESCRIPTION_INDEX = 1

87 CODES_INDEX = 2

88

89 icmp_messages = {

90 DESTINATION_UNREACHABLE : (ERROR_MESSAGE, "Destination unreachable",

91 { NO_ROUTE_TO_DESTINATION : "No route to destination",

92 ADMINISTRATIVELY_PROHIBITED : "Administratively prohibited",

93 BEYOND_SCOPE_OF_SOURCE_ADDRESS : "Beyond scope of source address",

94 ADDRESS_UNREACHABLE : "Address unreachable",

95 PORT_UNREACHABLE : "Port unreachable",

96 SOURCE_ADDRESS_FAILED_INGRESS_EGRESS_POLICY : "Source address failed ingress/egress policy",

97 REJECT_ROUTE_TO_DESTINATION : "Reject route to destination"

98 }),

99 PACKET_TOO_BIG : (ERROR_MESSAGE, "Packet too big", None),

100 TIME_EXCEEDED : (ERROR_MESSAGE, "Time exceeded",

101 {HOP_LIMIT_EXCEEDED_IN_TRANSIT : "Hop limit exceeded in transit",

102 FRAGMENT_REASSEMBLY_TIME_EXCEEDED : "Fragment reassembly time exceeded"

103 }),

104 PARAMETER_PROBLEM : (ERROR_MESSAGE, "Parameter problem",

105 {

106 ERRONEOUS_HEADER_FIELD_ENCOUNTERED : "Erroneous header field encountered",

107 UNRECOGNIZED_NEXT_HEADER_TYPE_ENCOUNTERED : "Unrecognized Next Header type encountered",

108 UNRECOGNIZED_IPV6_OPTION_ENCOUNTERED : "Unrecognized IPv6 Option Encountered"

109 }),

110 ECHO_REQUEST : (INFORMATIONAL_MESSAGE, "Echo request", None),

111 ECHO_REPLY : (INFORMATIONAL_MESSAGE, "Echo reply", None),

112 ROUTER_SOLICITATION : (INFORMATIONAL_MESSAGE, "Router Solicitation", None),

113 ROUTER_ADVERTISEMENT : (INFORMATIONAL_MESSAGE, "Router Advertisement", None),

114 NEIGHBOR_SOLICITATION : (INFORMATIONAL_MESSAGE, "Neighbor Solicitation", None),

115 NEIGHBOR_ADVERTISEMENT : (INFORMATIONAL_MESSAGE, "Neighbor Advertisement", None),

116 REDIRECT_MESSAGE : (INFORMATIONAL_MESSAGE, "Redirect Message", None),

117 NODE_INFORMATION_QUERY: (INFORMATIONAL_MESSAGE, "Node Information Query", None),

118 NODE_INFORMATION_REPLY: (INFORMATIONAL_MESSAGE, "Node Information Reply", None),

119 }

120

121

122

123

124############################################################################

125 def __init__(self, buffer = None):

126 Header.__init__(self, self.HEADER_SIZE)

127 if (buffer):

128 self.load_header(buffer)

129

130 def get_header_size(self):

131 return self.HEADER_SIZE

132

133 def get_ip_protocol_number(self):

134 return self.IP_PROTOCOL_NUMBER

135

136 def __str__(self):

137 type = self.get_type()

138 code = self.get_code()

139 checksum = self.get_checksum()

140

141 s = "ICMP6 - Type: " + str(type) + " - " + self.__get_message_description() + "\n"

142 s += "Code: " + str(code)

143 if (self.__get_code_description() != ""):

144 s += " - " + self.__get_code_description()

145 s += "\n"

146 s += "Checksum: " + str(checksum) + "\n"

147 return s

148

149 def __get_message_description(self):

150 return self.icmp_messages[self.get_type()][self.DESCRIPTION_INDEX]

151

152 def __get_code_description(self):

153 code_dictionary = self.icmp_messages[self.get_type()][self.CODES_INDEX]

154 if (code_dictionary is None):

155 return ""

156 else:

157 return code_dictionary[self.get_code()]

158

159############################################################################

160 def get_type(self):

161 return (self.get_byte(0))

162

163 def get_code(self):

164 return (self.get_byte(1))

165

166 def get_checksum(self):

167 return (self.get_word(2))

168

169############################################################################

170 def set_type(self, type):

171 self.set_byte(0, type)

172

173 def set_code(self, code):

174 self.set_byte(1, code)

175

176 def set_checksum(self, checksum):

177 self.set_word(2, checksum)

178

179############################################################################

180 def calculate_checksum(self):

181 #Initialize the checksum value to 0 to yield a correct calculation

182 self.set_checksum(0)

183 #Fetch the pseudo header from the IP6 parent packet

184 pseudo_header = self.parent().get_pseudo_header()

185 #Fetch the ICMP data

186 icmp_header = self.get_bytes()

187 #Build an array of bytes concatenating the pseudo_header, the ICMP header and the ICMP data (if present)

188 checksum_array = array.array('B')

189 checksum_array.extend(pseudo_header)

190 checksum_array.extend(icmp_header)

191 if (self.child()): 191 ↛ 195line 191 didn't jump to line 195, because the condition on line 191 was never false

192 checksum_array.extend(self.child().get_bytes())

193

194 #Compute the checksum over that array

195 self.set_checksum(self.compute_checksum(checksum_array))

196

197 def is_informational_message(self):

198 return self.icmp_messages[self.get_type()][self.MSG_TYPE_INDEX] == self.INFORMATIONAL_MESSAGE

199

200 def is_error_message(self):

201 return self.icmp_messages[self.get_type()][self.MSG_TYPE_INDEX] == self.ERROR_MESSAGE

202

203 def is_well_formed(self):

204 well_formed = True

205

206 #Check that the message type is known

207 well_formed &= self.get_type() in self.icmp_messages.keys()

208

209 #Check that the code is known (zero, if there are no codes defined)

210 code_dictionary = self.icmp_messages[self.get_type()][self.CODES_INDEX]

211 if (code_dictionary is None):

212 well_formed &= self.get_code() == 0

213 else:

214 well_formed &= self.get_code() in code_dictionary.keys()

215

216 return well_formed

217

218############################################################################

219

220 @classmethod

221 def Echo_Request(class_object, id, sequence_number, arbitrary_data = None):

222 return class_object.__build_echo_message(ICMP6.ECHO_REQUEST, id, sequence_number, arbitrary_data)

223

224 @classmethod

225 def Echo_Reply(class_object, id, sequence_number, arbitrary_data = None):

226 return class_object.__build_echo_message(ICMP6.ECHO_REPLY, id, sequence_number, arbitrary_data)

227

228 @classmethod

229 def __build_echo_message(class_object, type, id, sequence_number, arbitrary_data):

230 #Build ICMP6 header

231 icmp_packet = ICMP6()

232 icmp_packet.set_type(type)

233 icmp_packet.set_code(0)

234

235 #Pack ICMP payload

236 icmp_bytes = struct.pack('>H', id)

237 icmp_bytes += struct.pack('>H', sequence_number)

238 if (arbitrary_data is not None): 238 ↛ 240line 238 didn't jump to line 240, because the condition on line 238 was never false

239 icmp_bytes += array_tobytes(array.array('B', arbitrary_data))

240 icmp_payload = Data()

241 icmp_payload.set_data(icmp_bytes)

242

243 #Link payload to header

244 icmp_packet.contains(icmp_payload)

245

246 return icmp_packet

247

248

249############################################################################

250 @classmethod

251 def Destination_Unreachable(class_object, code, originating_packet_data = None):

252 unused_bytes = [0x00, 0x00, 0x00, 0x00]

253 return class_object.__build_error_message(ICMP6.DESTINATION_UNREACHABLE, code, unused_bytes, originating_packet_data)

254

255 @classmethod

256 def Packet_Too_Big(class_object, MTU, originating_packet_data = None):

257 MTU_bytes = struct.pack('!L', MTU)

258 return class_object.__build_error_message(ICMP6.PACKET_TOO_BIG, 0, MTU_bytes, originating_packet_data)

259

260 @classmethod

261 def Time_Exceeded(class_object, code, originating_packet_data = None):

262 unused_bytes = [0x00, 0x00, 0x00, 0x00]

263 return class_object.__build_error_message(ICMP6.TIME_EXCEEDED, code, unused_bytes, originating_packet_data)

264

265 @classmethod

266 def Parameter_Problem(class_object, code, pointer, originating_packet_data = None):

267 pointer_bytes = struct.pack('!L', pointer)

268 return class_object.__build_error_message(ICMP6.PARAMETER_PROBLEM, code, pointer_bytes, originating_packet_data)

269

270 @classmethod

271 def __build_error_message(class_object, type, code, data, originating_packet_data):

272 #Build ICMP6 header

273 icmp_packet = ICMP6()

274 icmp_packet.set_type(type)

275 icmp_packet.set_code(code)

276

277 #Pack ICMP payload

278 icmp_bytes = array_tobytes(array.array('B', data))

279 if (originating_packet_data is not None): 279 ↛ 281line 279 didn't jump to line 281, because the condition on line 279 was never false

280 icmp_bytes += array_tobytes(array.array('B', originating_packet_data))

281 icmp_payload = Data()

282 icmp_payload.set_data(icmp_bytes)

283

284 #Link payload to header

285 icmp_packet.contains(icmp_payload)

286

287 return icmp_packet

288

289############################################################################

290

291 @classmethod

292 def Neighbor_Solicitation(class_object, target_address):

293 return class_object.__build_neighbor_message(ICMP6.NEIGHBOR_SOLICITATION, target_address)

294

295 @classmethod

296 def Neighbor_Advertisement(class_object, target_address):

297 return class_object.__build_neighbor_message(ICMP6.NEIGHBOR_ADVERTISEMENT, target_address)

298

299 @classmethod

300 def __build_neighbor_message(class_object, msg_type, target_address):

301 #Build ICMP6 header

302 icmp_packet = ICMP6()

303 icmp_packet.set_type(msg_type)

304 icmp_packet.set_code(0)

305

306 # Flags + Reserved

307 icmp_bytes = array_tobytes(array.array('B', [0x00] * 4))

308

309 # Target Address: The IP address of the target of the solicitation.

310 # It MUST NOT be a multicast address.

311 icmp_bytes += array_tobytes(array.array('B', IP6_Address(target_address).as_bytes()))

312

313 icmp_payload = Data()

314 icmp_payload.set_data(icmp_bytes)

315

316 #Link payload to header

317 icmp_packet.contains(icmp_payload)

318

319 return icmp_packet

320

321############################################################################

322

323 def get_target_address(self):

324 return IP6_Address(self.child().get_bytes()[4:20])

325

326 def set_target_address(self, target_address):

327 address = IP6_Address(target_address)

328 payload_bytes = self.child().get_bytes()

329 payload_bytes[4:20] = address.get_bytes()

330 self.child().set_bytes(payload_bytes)

331

332 # 0 1 2 3 4 5 6 7

333 # +-+-+-+-+-+-+-+-+

334 # |R|S|O|reserved |

335 # +-+-+-+-+-+-+-+-+

336

337 def get_neighbor_advertisement_flags(self):

338 return self.child().get_byte(0)

339

340 def set_neighbor_advertisement_flags(self, flags):

341 self.child().set_byte(0, flags)

342

343 def get_router_flag(self):

344 return (self.get_neighbor_advertisement_flags() & 0x80) != 0

345

346 def set_router_flag(self, flag_value):

347 curr_flags = self.get_neighbor_advertisement_flags()

348 if flag_value:

349 curr_flags |= 0x80

350 else:

351 curr_flags &= ~0x80

352 self.set_neighbor_advertisement_flags(curr_flags)

353

354 def get_solicited_flag(self):

355 return (self.get_neighbor_advertisement_flags() & 0x40) != 0

356

357 def set_solicited_flag(self, flag_value):

358 curr_flags = self.get_neighbor_advertisement_flags()

359 if flag_value:

360 curr_flags |= 0x40

361 else:

362 curr_flags &= ~0x40

363 self.set_neighbor_advertisement_flags(curr_flags)

364

365 def get_override_flag(self):

366 return (self.get_neighbor_advertisement_flags() & 0x20) != 0

367

368 def set_override_flag(self, flag_value):

369 curr_flags = self.get_neighbor_advertisement_flags()

370 if flag_value:

371 curr_flags |= 0x20

372 else:

373 curr_flags &= ~0x20

374 self.set_neighbor_advertisement_flags(curr_flags)

375

376############################################################################

377 @classmethod

378 def Node_Information_Query(class_object, code, payload = None):

379 return class_object.__build_node_information_message(ICMP6.NODE_INFORMATION_QUERY, code, payload)

380

381 @classmethod

382 def Node_Information_Reply(class_object, code, payload = None):

383 return class_object.__build_node_information_message(ICMP6.NODE_INFORMATION_REPLY, code, payload)

384

385 @classmethod

386 def __build_node_information_message(class_object, type, code, payload = None):

387 #Build ICMP6 header

388 icmp_packet = ICMP6()

389 icmp_packet.set_type(type)

390 icmp_packet.set_code(code)

391

392 #Pack ICMP payload

393 qtype = 0

394 flags = 0

395 nonce = [0x00] * 8

396

397 icmp_bytes = struct.pack('>H', qtype)

398 icmp_bytes += struct.pack('>H', flags)

399 icmp_bytes += array_tobytes(array.array('B', nonce))

400

401 if payload is not None:

402 icmp_bytes += array_tobytes(array.array('B', payload))

403

404 icmp_payload = Data()

405 icmp_payload.set_data(icmp_bytes)

406

407 #Link payload to header

408 icmp_packet.contains(icmp_payload)

409

410 return icmp_packet

411

412 def get_qtype(self):

413 return self.child().get_word(0)

414

415 def set_qtype(self, qtype):

416 self.child().set_word(0, qtype)

417

418 def get_nonce(self):

419 return self.child().get_bytes()[4:12]

420

421 def set_nonce(self, nonce):

422 payload_bytes = self.child().get_bytes()

423 payload_bytes[4:12] = array.array('B', nonce)

424 self.child().set_bytes(payload_bytes)

425

426 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5

427 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

428 # | unused |G|S|L|C|A|T|

429 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

430

431 def get_flags(self):

432 return self.child().get_word(2)

433

434 def set_flags(self, flags):

435 self.child().set_word(2, flags)

436

437 def get_flag_T(self):

438 return (self.get_flags() & 0x0001) != 0

439

440 def set_flag_T(self, flag_value):

441 curr_flags = self.get_flags()

442 if flag_value:

443 curr_flags |= 0x0001

444 else:

445 curr_flags &= ~0x0001

446 self.set_flags(curr_flags)

447

448 def get_flag_A(self):

449 return (self.get_flags() & 0x0002) != 0

450

451 def set_flag_A(self, flag_value):

452 curr_flags = self.get_flags()

453 if flag_value:

454 curr_flags |= 0x0002

455 else:

456 curr_flags &= ~0x0002

457 self.set_flags(curr_flags)

458

459 def get_flag_C(self):

460 return (self.get_flags() & 0x0004) != 0

461

462 def set_flag_C(self, flag_value):

463 curr_flags = self.get_flags()

464 if flag_value:

465 curr_flags |= 0x0004

466 else:

467 curr_flags &= ~0x0004

468 self.set_flags(curr_flags)

469

470 def get_flag_L(self):

471 return (self.get_flags() & 0x0008) != 0

472

473 def set_flag_L(self, flag_value):

474 curr_flags = self.get_flags()

475 if flag_value:

476 curr_flags |= 0x0008

477 else:

478 curr_flags &= ~0x0008

479 self.set_flags(curr_flags)

480

481 def get_flag_S(self):

482 return (self.get_flags() & 0x0010) != 0

483

484 def set_flag_S(self, flag_value):

485 curr_flags = self.get_flags()

486 if flag_value:

487 curr_flags |= 0x0010

488 else:

489 curr_flags &= ~0x0010

490 self.set_flags(curr_flags)

491

492 def get_flag_G(self):

493 return (self.get_flags() & 0x0020) != 0

494

495 def set_flag_G(self, flag_value):

496 curr_flags = self.get_flags()

497 if flag_value:

498 curr_flags |= 0x0020

499 else:

500 curr_flags &= ~0x0020

501 self.set_flags(curr_flags)

502

503 def set_node_information_data(self, data):

504 payload_bytes = self.child().get_bytes()

505 payload_bytes[12:] = array.array('B', data)

506 self.child().set_bytes(payload_bytes)

507

508 def get_note_information_data(self):

509 return self.child().get_bytes()[12:]

510

511############################################################################

512 def get_echo_id(self):

513 return self.child().get_word(0)

514

515 def get_echo_sequence_number(self):

516 return self.child().get_word(2)

517

518 def get_echo_arbitrary_data(self):

519 return self.child().get_bytes()[4:]

520

521 def get_mtu(self):

522 return self.child().get_long(0)

523

524 def get_parm_problem_pointer(self):

525 return self.child().get_long(0)

526

527 def get_originating_packet_data(self):

528 return self.child().get_bytes()[4:]