Hide keyboard shortcuts

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 

10import array 

11import struct 

12 

13from impacket.ImpactPacket import Header, Data, array_tobytes 

14from impacket.IP6_Address import IP6_Address 

15 

16 

17class 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:]