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# 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 

8import array 

9import struct 

10 

11from impacket.ImpactPacket import Header, Data, array_tobytes 

12from impacket.IP6_Address import IP6_Address 

13 

14 

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