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

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

## 312 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# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.

2#

3# This software is provided under under a slightly modified version

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

5# for more information.

6#

7

8 import array

9 import struct

10

11 from impacket.ImpactPacket import Header, Data, array_tobytes

12 from impacket.IP6_Address import IP6_Address

13

14

15 class ICMP6(Header):

16 #IP Protocol number for ICMP6

17 IP_PROTOCOL_NUMBER = 58

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

19

20 #Size of ICMP6 header (excluding payload)

21 HEADER_SIZE = 4

22

23 #ICMP6 Message Type numbers

24 DESTINATION_UNREACHABLE = 1

25 PACKET_TOO_BIG = 2

26 TIME_EXCEEDED = 3

27 PARAMETER_PROBLEM = 4

28 ECHO_REQUEST = 128

29 ECHO_REPLY = 129

30 ROUTER_SOLICITATION = 133

31 ROUTER_ADVERTISEMENT = 134

32 NEIGHBOR_SOLICITATION = 135

33 NEIGHBOR_ADVERTISEMENT = 136

34 REDIRECT_MESSAGE = 137

35 NODE_INFORMATION_QUERY = 139

36 NODE_INFORMATION_REPLY = 140

37

38 #Destination Unreachable codes

39 NO_ROUTE_TO_DESTINATION = 0

40 ADMINISTRATIVELY_PROHIBITED = 1

41 BEYOND_SCOPE_OF_SOURCE_ADDRESS = 2

42 ADDRESS_UNREACHABLE = 3

43 PORT_UNREACHABLE = 4

44 SOURCE_ADDRESS_FAILED_INGRESS_EGRESS_POLICY = 5

45 REJECT_ROUTE_TO_DESTINATION = 6

46

47 #Time Exceeded codes

48 HOP_LIMIT_EXCEEDED_IN_TRANSIT = 0

49 FRAGMENT_REASSEMBLY_TIME_EXCEEDED = 1

50

51 #Parameter problem codes

52 ERRONEOUS_HEADER_FIELD_ENCOUNTERED = 0

53 UNRECOGNIZED_NEXT_HEADER_TYPE_ENCOUNTERED = 1

54 UNRECOGNIZED_IPV6_OPTION_ENCOUNTERED = 2

55

56 #Node Information codes

57 NODE_INFORMATION_QUERY_IPV6 = 0

58 NODE_INFORMATION_QUERY_NAME_OR_EMPTY = 1

59 NODE_INFORMATION_QUERY_IPV4 = 2

60 NODE_INFORMATION_REPLY_SUCCESS = 0

61 NODE_INFORMATION_REPLY_REFUSED = 1

62 NODE_INFORMATION_REPLY_UNKNOWN_QTYPE = 2

63

64 #Node Information qtypes

65 NODE_INFORMATION_QTYPE_NOOP = 0

66 NODE_INFORMATION_QTYPE_UNUSED = 1

67 NODE_INFORMATION_QTYPE_NODENAME = 2

68 NODE_INFORMATION_QTYPE_NODEADDRS = 3

69 NODE_INFORMATION_QTYPE_IPv4ADDRS = 4

70

71 #ICMP Message semantic types (error or informational)

72 ERROR_MESSAGE = 0

73 INFORMATIONAL_MESSAGE = 1

74

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

76 #Key: ICMP message number

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

78 #Codes dictionary

79 #Key: Code number

80 #Data: Text description

81

82 #ICMP message dictionary tuple indexes

83 MSG_TYPE_INDEX = 0

84 DESCRIPTION_INDEX = 1

85 CODES_INDEX = 2

86

87 icmp_messages = {

88 DESTINATION_UNREACHABLE : (ERROR_MESSAGE, "Destination unreachable",

89 { NO_ROUTE_TO_DESTINATION : "No route to destination",

90 ADMINISTRATIVELY_PROHIBITED : "Administratively prohibited",

91 BEYOND_SCOPE_OF_SOURCE_ADDRESS : "Beyond scope of source address",

92 ADDRESS_UNREACHABLE : "Address unreachable",

93 PORT_UNREACHABLE : "Port unreachable",

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

95 REJECT_ROUTE_TO_DESTINATION : "Reject route to destination"

96 }),

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

98 TIME_EXCEEDED : (ERROR_MESSAGE, "Time exceeded",

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

100 FRAGMENT_REASSEMBLY_TIME_EXCEEDED : "Fragment reassembly time exceeded"

101 }),

102 PARAMETER_PROBLEM : (ERROR_MESSAGE, "Parameter problem",

103 {

104 ERRONEOUS_HEADER_FIELD_ENCOUNTERED : "Erroneous header field encountered",

105 UNRECOGNIZED_NEXT_HEADER_TYPE_ENCOUNTERED : "Unrecognized Next Header type encountered",

106 UNRECOGNIZED_IPV6_OPTION_ENCOUNTERED : "Unrecognized IPv6 Option Encountered"

107 }),

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

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

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

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

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

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

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

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

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

117 }

118

119

120

121

122############################################################################

123 def __init__(self, buffer = None):

124 Header.__init__(self, self.HEADER_SIZE)

125 if (buffer):

126 self.load_header(buffer)

127

128 def get_header_size(self):

129 return self.HEADER_SIZE

130

131 def get_ip_protocol_number(self):

132 return self.IP_PROTOCOL_NUMBER

133

134 def __str__(self):

135 type = self.get_type()

136 code = self.get_code()

137 checksum = self.get_checksum()

138

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

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

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

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

143 s += "\n"

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

145 return s

146

147 def __get_message_description(self):

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

149

150 def __get_code_description(self):

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

152 if (code_dictionary is None):

153 return ""

154 else:

155 return code_dictionary[self.get_code()]

156

157############################################################################

158 def get_type(self):

159 return (self.get_byte(0))

160

161 def get_code(self):

162 return (self.get_byte(1))

163

164 def get_checksum(self):

165 return (self.get_word(2))

166

167############################################################################

168 def set_type(self, type):

169 self.set_byte(0, type)

170

171 def set_code(self, code):

172 self.set_byte(1, code)

173

174 def set_checksum(self, checksum):

175 self.set_word(2, checksum)

176

177############################################################################

178 def calculate_checksum(self):

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

180 self.set_checksum(0)

181 #Fetch the pseudo header from the IP6 parent packet

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

183 #Fetch the ICMP data

184 icmp_header = self.get_bytes()

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

186 checksum_array = array.array('B')

187 checksum_array.extend(pseudo_header)

188 checksum_array.extend(icmp_header)

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

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

191

192 #Compute the checksum over that array

193 self.set_checksum(self.compute_checksum(checksum_array))

194

195 def is_informational_message(self):

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

197

198 def is_error_message(self):

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

200

201 def is_well_formed(self):

202 well_formed = True

203

204 #Check that the message type is known

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

206

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

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

209 if (code_dictionary is None):

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

211 else:

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

213

214 return well_formed

215

216############################################################################

217

218 @classmethod

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

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

221

222 @classmethod

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

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

225

226 @classmethod

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

228 #Build ICMP6 header

229 icmp_packet = ICMP6()

230 icmp_packet.set_type(type)

231 icmp_packet.set_code(0)

232

233 #Pack ICMP payload

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

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

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

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

238 icmp_payload = Data()

239 icmp_payload.set_data(icmp_bytes)

240

241 #Link payload to header

242 icmp_packet.contains(icmp_payload)

243

244 return icmp_packet

245

246

247############################################################################

248 @classmethod

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

250 unused_bytes = [0x00, 0x00, 0x00, 0x00]

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

252

253 @classmethod

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

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

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

257

258 @classmethod

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

260 unused_bytes = [0x00, 0x00, 0x00, 0x00]

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

262

263 @classmethod

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

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

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

267

268 @classmethod

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

270 #Build ICMP6 header

271 icmp_packet = ICMP6()

272 icmp_packet.set_type(type)

273 icmp_packet.set_code(code)

274

275 #Pack ICMP payload

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

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

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

279 icmp_payload = Data()

280 icmp_payload.set_data(icmp_bytes)

281

282 #Link payload to header

283 icmp_packet.contains(icmp_payload)

284

285 return icmp_packet

286

287############################################################################

288

289 @classmethod

290 def Neighbor_Solicitation(class_object, target_address):

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

292

293 @classmethod

294 def Neighbor_Advertisement(class_object, target_address):

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

296

297 @classmethod

298 def __build_neighbor_message(class_object, msg_type, target_address):

299 #Build ICMP6 header

300 icmp_packet = ICMP6()

301 icmp_packet.set_type(msg_type)

302 icmp_packet.set_code(0)

303

304 # Flags + Reserved

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

306

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

308 # It MUST NOT be a multicast address.

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

310

311 icmp_payload = Data()

312 icmp_payload.set_data(icmp_bytes)

313

314 #Link payload to header

315 icmp_packet.contains(icmp_payload)

316

317 return icmp_packet

318

319############################################################################

320

321 def get_target_address(self):

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

323

324 def set_target_address(self, target_address):

325 address = IP6_Address(target_address)

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

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

328 self.child().set_bytes(payload_bytes)

329

330 # 0 1 2 3 4 5 6 7

331 # +-+-+-+-+-+-+-+-+

332 # |R|S|O|reserved |

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

334

335 def get_neighbor_advertisement_flags(self):

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

337

338 def set_neighbor_advertisement_flags(self, flags):

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

340

341 def get_router_flag(self):

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

343

344 def set_router_flag(self, flag_value):

345 curr_flags = self.get_neighbor_advertisement_flags()

346 if flag_value:

347 curr_flags |= 0x80

348 else:

349 curr_flags &= ~0x80

350 self.set_neighbor_advertisement_flags(curr_flags)

351

352 def get_solicited_flag(self):

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

354

355 def set_solicited_flag(self, flag_value):

356 curr_flags = self.get_neighbor_advertisement_flags()

357 if flag_value:

358 curr_flags |= 0x40

359 else:

360 curr_flags &= ~0x40

361 self.set_neighbor_advertisement_flags(curr_flags)

362

363 def get_override_flag(self):

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

365

366 def set_override_flag(self, flag_value):

367 curr_flags = self.get_neighbor_advertisement_flags()

368 if flag_value:

369 curr_flags |= 0x20

370 else:

371 curr_flags &= ~0x20

372 self.set_neighbor_advertisement_flags(curr_flags)

373

374############################################################################

375 @classmethod

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

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

378

379 @classmethod

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

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

382

383 @classmethod

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

385 #Build ICMP6 header

386 icmp_packet = ICMP6()

387 icmp_packet.set_type(type)

388 icmp_packet.set_code(code)

389

390 #Pack ICMP payload

391 qtype = 0

392 flags = 0

393 nonce = [0x00] * 8

394

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

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

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

398

399 if payload is not None:

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

401

402 icmp_payload = Data()

403 icmp_payload.set_data(icmp_bytes)

404

405 #Link payload to header

406 icmp_packet.contains(icmp_payload)

407

408 return icmp_packet

409

410 def get_qtype(self):

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

412

413 def set_qtype(self, qtype):

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

415

416 def get_nonce(self):

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

418

419 def set_nonce(self, nonce):

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

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

422 self.child().set_bytes(payload_bytes)

423

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

425 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

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

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

428

429 def get_flags(self):

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

431

432 def set_flags(self, flags):

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

434

435 def get_flag_T(self):

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

437

438 def set_flag_T(self, flag_value):

439 curr_flags = self.get_flags()

440 if flag_value:

441 curr_flags |= 0x0001

442 else:

443 curr_flags &= ~0x0001

444 self.set_flags(curr_flags)

445

446 def get_flag_A(self):

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

448

449 def set_flag_A(self, flag_value):

450 curr_flags = self.get_flags()

451 if flag_value:

452 curr_flags |= 0x0002

453 else:

454 curr_flags &= ~0x0002

455 self.set_flags(curr_flags)

456

457 def get_flag_C(self):

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

459

460 def set_flag_C(self, flag_value):

461 curr_flags = self.get_flags()

462 if flag_value:

463 curr_flags |= 0x0004

464 else:

465 curr_flags &= ~0x0004

466 self.set_flags(curr_flags)

467

468 def get_flag_L(self):

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

470

471 def set_flag_L(self, flag_value):

472 curr_flags = self.get_flags()

473 if flag_value:

474 curr_flags |= 0x0008

475 else:

476 curr_flags &= ~0x0008

477 self.set_flags(curr_flags)

478

479 def get_flag_S(self):

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

481

482 def set_flag_S(self, flag_value):

483 curr_flags = self.get_flags()

484 if flag_value:

485 curr_flags |= 0x0010

486 else:

487 curr_flags &= ~0x0010

488 self.set_flags(curr_flags)

489

490 def get_flag_G(self):

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

492

493 def set_flag_G(self, flag_value):

494 curr_flags = self.get_flags()

495 if flag_value:

496 curr_flags |= 0x0020

497 else:

498 curr_flags &= ~0x0020

499 self.set_flags(curr_flags)

500

501 def set_node_information_data(self, data):

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

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

504 self.child().set_bytes(payload_bytes)

505

506 def get_note_information_data(self):

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

508

509############################################################################

510 def get_echo_id(self):

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

512

513 def get_echo_sequence_number(self):

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

515

516 def get_echo_arbitrary_data(self):

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

518

519 def get_mtu(self):

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

521

522 def get_parm_problem_pointer(self):

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

524

525 def get_originating_packet_data(self):

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